Completed
Push — patch_1-1-4 ( 3f780f...826343 )
by Emanuele
25:17 queued 11:40
created

ProfileOptions_Controller::action_editBuddies()   C

Complexity

Conditions 14
Paths 48

Size

Total Lines 105

Duplication

Lines 19
Ratio 18.1 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 0
Metric Value
cc 14
nc 48
nop 0
dl 19
loc 105
rs 5.0133
c 0
b 0
f 0
ccs 0
cts 62
cp 0
crap 210

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
 * @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 1.1.4
17
 *
18
 */
19
20
/**
21
 * ProfileOptions_Controller Class.
22
 *
23
 * - Does the job of showing and editing people's profiles.
24
 * - Interface to buddy list, ignore list, notifications, authentication options, forum profile
25
 * account settings, etc
26
 */
27
class ProfileOptions_Controller extends Action_Controller
28
{
29
	/**
30
	 * Returns the profile fields for a given area
31
	 *
32
	 * @param string $area
33
	 * @return array|mixed
34
	 */
35
	public static function getFields($area)
36
	{
37
		global $modSettings;
38
39
		$fields = array(
40
			'account' => array(
41
				'fields' => array(
42
					'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr',
43
					'id_group', 'hr',
44
					'email_address', 'hide_email', 'show_online', 'hr',
45
					'passwrd1', 'passwrd2', 'hr',
46
					'secret_question', 'secret_answer',
47
				),
48
				'hook' => 'account'
49
			),
50
			'account_otp' => array(
51
				'fields' => array(
52
					'member_name', 'real_name', 'date_registered', 'posts', 'lngfile', 'hr',
53
					'id_group', 'hr',
54
					'email_address', 'hide_email', 'show_online', 'hr',
55
					'passwrd1', 'passwrd2', 'hr',
56
					'secret_question', 'secret_answer', 'hr',
57
					'enable_otp', 'otp_secret', 'hr'
58
				),
59
				'hook' => 'account'
60
			),
61
			'forumprofile' => array(
62
				'fields' => array(
63
					'avatar_choice', 'hr',
64
					'bday1', 'usertitle','hr',
65
					'signature', 'hr',
66
					'karma_good', 'hr',
67
					'website_title', 'website_url',
68
				),
69
				'hook' => 'forum'
70
			),
71
			'theme' => array(
72
				'fields' => array(
73
					'id_theme', 'smiley_set', 'hr',
74
					'time_format', 'time_offset', 'hr',
75
					'theme_settings',
76
				),
77
				'hook' => 'themepick'
78
			),
79
			'contactprefs' => array(
80
				'fields' => array(
81
					'receive_from',
82
					'hr',
83
					'pm_settings',
84
				),
85
				'hook' => 'pmprefs'
86
			),
87
			'registration' => array(
88
				'fields' => !empty($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array(),
89
				'hook' => 'registration'
90
			)
91
		);
92
93
		if (isset($fields[$area]))
94
		{
95
			return $fields[$area];
96
		}
97
		else
98
		{
99
			return array();
100
		}
101
	}
102
103
	/**
104
	 * Member id for the profile being viewed
105
	 * @var int
106
	 */
107
	private $_memID = 0;
108
109
	/**
110
	 * Called before all other methods when coming from the dispatcher or
111
	 * action class.
112
	 *
113
	 * - If you initiate the class outside of those methods, call this method.
114
	 * or setup the class yourself else a horrible fate awaits you
115
	 */
116
	public function pre_dispatch()
117
	{
118
		$this->_memID = currentMemberID();
119
	}
120
121
	/**
122
	 * Default method, if another action is not called by the menu.
123
	 *
124
	 * @see Action_Controller::action_index()
125
	 */
126
	public function action_index()
127
	{
128
		// action_account() is the first to do
129
		// these subactions are mostly routed to from the profile
130
		// menu though.
131
	}
132
133
	/**
134
	 * Show all the users buddies, as well as a add/delete interface.
135
	 */
136
	public function action_editBuddyIgnoreLists()
137
	{
138
		global $context, $txt, $modSettings;
139
140
		// Do a quick check to ensure people aren't getting here illegally!
141
		if (!$context['user']['is_owner'] || empty($modSettings['enable_buddylist']))
142
			throw new Elk_Exception('no_access', false);
143
144
		loadTemplate('ProfileOptions');
145
146
		// Can we email the user direct?
147
		$context['can_moderate_forum'] = allowedTo('moderate_forum');
148
		$context['can_send_email'] = allowedTo('send_email_to_members');
149
150
		$subActions = array(
151
			'buddies' => array($this, 'action_editBuddies'),
152
			'ignore' => array($this, 'action_editIgnoreList'),
153
		);
154
155
		// Set a subaction
156
		$action = new Action();
157
		$subAction = $action->initialize($subActions, 'buddies');
158
159
		// Create the tabs for the template.
160
		$context[$context['profile_menu_name']]['tab_data'] = array(
161
			'title' => $txt['editBuddyIgnoreLists'],
162
			'description' => $txt['buddy_ignore_desc'],
163
			'class' => 'profile',
164
			'tabs' => array(
165
				'buddies' => array(),
166
				'ignore' => array(),
167
			),
168
		);
169
170
		// Pass on to the actual function.
171
		$action->dispatch($subAction);
172
	}
173
174
	/**
175
	 * Show all the users buddies, as well as a add/delete interface.
176
	 *
177
	 * @uses template_editBuddies()
178
	 */
179
	public function action_editBuddies()
180
	{
181
		global $context, $user_profile, $memberContext;
182
183
		loadTemplate('ProfileOptions');
184
185
		// We want to view what we're doing :P
186
		$context['sub_template'] = 'editBuddies';
187
188
		// Use suggest to find the right buddies
189
		loadJavascriptFile('suggest.js', array('defer' => true));
190
191
		// For making changes!
192
		$buddiesArray = explode(',', $user_profile[$this->_memID]['buddy_list']);
193
		foreach ($buddiesArray as $k => $dummy)
194
		{
195
			if ($dummy === '')
196
				unset($buddiesArray[$k]);
197
		}
198
199
		// Removing a buddy?
200
		if (isset($this->_req->query->remove))
201
		{
202
			checkSession('get');
203
204
			call_integration_hook('integrate_remove_buddy', array($this->_memID));
205
206
			// Heh, I'm lazy, do it the easy way...
207
			foreach ($buddiesArray as $key => $buddy)
208
			{
209
				if ($buddy == (int) $this->_req->query->remove)
210
					unset($buddiesArray[$key]);
211
			}
212
213
			// Make the changes.
214
			$user_profile[$this->_memID]['buddy_list'] = implode(',', $buddiesArray);
215
			require_once(SUBSDIR . '/Members.subs.php');
216
			updateMemberData($this->_memID, array('buddy_list' => $user_profile[$this->_memID]['buddy_list']));
217
218
			// Redirect off the page because we don't like all this ugly query stuff to stick in the history.
219
			redirectexit('action=profile;area=lists;sa=buddies;u=' . $this->_memID);
220
		}
221
		// Or adding a new one
222
		elseif (isset($this->_req->post->new_buddy))
223
		{
224
			checkSession();
225
226
			// Prepare the string for extraction...
227
			$new_buddy = strtr(Util::htmlspecialchars($this->_req->post->new_buddy, ENT_QUOTES), array('&quot;' => '"'));
228
			preg_match_all('~"([^"]+)"~', $new_buddy, $matches);
229
			$new_buddies = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $new_buddy))));
230
231 View Code Duplication
			foreach ($new_buddies as $k => $dummy)
232
			{
233
				$new_buddies[$k] = strtr(trim($new_buddies[$k]), array('\'' => '&#039;'));
234
235
				if (strlen($new_buddies[$k]) == 0 || in_array($new_buddies[$k], array($user_profile[$this->_memID]['member_name'], $user_profile[$this->_memID]['real_name'])))
236
					unset($new_buddies[$k]);
237
			}
238
239
			call_integration_hook('integrate_add_buddies', array($this->_memID, &$new_buddies));
240
241
			if (!empty($new_buddies))
242
			{
243
				// Now find out the id_member of the buddy.
244
				require_once(SUBSDIR . '/ProfileOptions.subs.php');
245
				$new_buddiesArray = getBuddiesID($new_buddies);
246
				$old_buddiesArray = explode(',', $user_profile[$this->_memID]['buddy_list']);
247
				// Now update the current users buddy list.
248
				$user_profile[$this->_memID]['buddy_list'] = implode(',', array_filter(array_unique(array_merge($new_buddiesArray, $old_buddiesArray))));
249
250
				require_once(SUBSDIR . '/Members.subs.php');
251
				updateMemberData($this->_memID, array('buddy_list' => $user_profile[$this->_memID]['buddy_list']));
252
			}
253
254
			// Back to the buddy list!
255
			redirectexit('action=profile;area=lists;sa=buddies;u=' . $this->_memID);
256
		}
257
258
		// Get all the users "buddies"...
259
		$buddies = array();
260
261 View Code Duplication
		if (!empty($buddiesArray))
262
		{
263
			require_once(SUBSDIR . '/Members.subs.php');
264
			$result = getBasicMemberData($buddiesArray, array('sort' => 'real_name', 'limit' => substr_count($user_profile[$this->_memID]['buddy_list'], ',') + 1));
265
			foreach ($result as $row)
266
				$buddies[] = $row['id_member'];
267
		}
268
269
		$context['buddy_count'] = count($buddies);
270
271
		// Load all the members up.
272
		loadMemberData($buddies, false, 'profile');
273
274
		// Setup the context for each buddy.
275
		$context['buddies'] = array();
276 View Code Duplication
		foreach ($buddies as $buddy)
277
		{
278
			loadMemberContext($buddy, true);
279
			$context['buddies'][$buddy] = $memberContext[$buddy];
280
		}
281
282
		call_integration_hook('integrate_view_buddies', array($this->_memID));
283
	}
284
285
	/**
286
	 * Allows the user to view their ignore list,
287
	 *
288
	 * - Provides the option to manage members on it.
289
	 */
290
	public function action_editIgnoreList()
291
	{
292
		global $context, $user_profile, $memberContext;
293
294
		loadTemplate('ProfileOptions');
295
296
		// We want to view what we're doing :P
297
		$context['sub_template'] = 'editIgnoreList';
298
		loadJavascriptFile('suggest.js', array('defer' => true));
299
300
		// For making changes!
301
		$ignoreArray = explode(',', $user_profile[$this->_memID]['pm_ignore_list']);
302
		foreach ($ignoreArray as $k => $dummy)
303
		{
304
			if ($dummy === '')
305
				unset($ignoreArray[$k]);
306
		}
307
308
		// Removing a member from the ignore list?
309
		if (isset($this->_req->query->remove))
310
		{
311
			checkSession('get');
312
313
			// Heh, I'm lazy, do it the easy way...
314
			foreach ($ignoreArray as $key => $id_remove)
315
			{
316
				if ($id_remove == (int) $this->_req->query->remove)
317
					unset($ignoreArray[$key]);
318
			}
319
320
			// Make the changes.
321
			$user_profile[$this->_memID]['pm_ignore_list'] = implode(',', $ignoreArray);
322
			require_once(SUBSDIR . '/Members.subs.php');
323
			updateMemberData($this->_memID, array('pm_ignore_list' => $user_profile[$this->_memID]['pm_ignore_list']));
324
325
			// Redirect off the page because we don't like all this ugly query stuff to stick in the history.
326
			redirectexit('action=profile;area=lists;sa=ignore;u=' . $this->_memID);
327
		}
328
		elseif (isset($this->_req->post->new_ignore))
329
		{
330
			checkSession();
331
332
			// Prepare the string for extraction...
333
			$new_ignore = strtr(Util::htmlspecialchars($this->_req->post->new_ignore, ENT_QUOTES), array('&quot;' => '"'));
334
			preg_match_all('~"([^"]+)"~', $new_ignore, $matches);
335
			$new_entries = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $new_ignore))));
336
337 View Code Duplication
			foreach ($new_entries as $k => $dummy)
338
			{
339
				$new_entries[$k] = strtr(trim($new_entries[$k]), array('\'' => '&#039;'));
340
341
				if (strlen($new_entries[$k]) == 0 || in_array($new_entries[$k], array($user_profile[$this->_memID]['member_name'], $user_profile[$this->_memID]['real_name'])))
342
					unset($new_entries[$k]);
343
			}
344
345
			if (!empty($new_entries))
346
			{
347
				// Now find out the id_member for the members in question.
348
				require_once(SUBSDIR . '/ProfileOptions.subs.php');
349
				$ignoreArray = getBuddiesID($new_entries, false);
350
351
				// Now update the current users buddy list.
352
				$user_profile[$this->_memID]['pm_ignore_list'] = implode(',', $ignoreArray);
353
				require_once(SUBSDIR . '/Members.subs.php');
354
				updateMemberData($this->_memID, array('pm_ignore_list' => $user_profile[$this->_memID]['pm_ignore_list']));
355
			}
356
357
			// Back to the list of pitiful people!
358
			redirectexit('action=profile;area=lists;sa=ignore;u=' . $this->_memID);
359
		}
360
361
		// Initialise the list of members we're ignoring.
362
		$ignored = array();
363
364 View Code Duplication
		if (!empty($ignoreArray))
365
		{
366
			require_once(SUBSDIR . '/Members.subs.php');
367
			$result = getBasicMemberData($ignoreArray, array('sort' => 'real_name', 'limit' => substr_count($user_profile[$this->_memID]['pm_ignore_list'], ',') + 1));
368
			foreach ($result as $row)
369
				$ignored[] = $row['id_member'];
370
		}
371
372
		$context['ignore_count'] = count($ignored);
373
374
		// Load all the members up.
375
		loadMemberData($ignored, false, 'profile');
376
377
		// Setup the context for each buddy.
378
		$context['ignore_list'] = array();
379
		foreach ($ignored as $ignore_member)
380
		{
381
			loadMemberContext($ignore_member);
382
			$context['ignore_list'][$ignore_member] = $memberContext[$ignore_member];
383
		}
384
	}
385
386
	/**
387
	 * Allows the user to see or change their account info.
388
	 */
389
	public function action_account()
390
	{
391
		global $modSettings, $context, $txt;
392
393
		loadTemplate('ProfileOptions');
394
		$this->loadThemeOptions();
395
396
		if (allowedTo(array('profile_identity_own', 'profile_identity_any')))
397
			loadCustomFields($this->_memID, 'account');
398
399
		$context['sub_template'] = 'edit_options';
400
		$context['page_desc'] = $txt['account_info'];
401
402
		if (!empty($modSettings['enableOTP']))
403
		{
404
			$fields = self::getFields('account_otp');
405
			setupProfileContext($fields['fields'], $fields['hook']);
406
407
			loadJavascriptFile('qrcode.js');
408
			addInlineJavascript('
409
				var secret = document.getElementById("otp_secret").value;
410
411
				if (secret)
412
				{
413
					var qrcode = new QRCode("qrcode", {
414
						text: "otpauth://totp/' . $context['forum_name'] . '?secret=" + secret,
415
						width: 100,
416
						height: 100,
417
						colorDark : "#000000",
418
						colorLight : "#ffffff",
419
					});
420
				}
421
422
				/**
423
				* Generate a secret key for Google Authenticator
424
				*/
425
				function generateSecret() {
426
					var text = "",
427
						possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
428
						qr = document.getElementById("qrcode");
429
430
					for (var i = 0; i < 16; i++)
431
						text += possible.charAt(Math.floor(Math.random() * possible.length));
432
433
					document.getElementById("otp_secret").value = text;
434
435
					while (qr.firstChild) {
436
						qr.removeChild(qr.firstChild);
437
					}
438
439
					var qrcode = new QRCode("qrcode", {
440
						text: "otpauth://totp/' . $context['forum_name'] . '?secret=" + text,
441
						width: 100,
442
						height: 100,
443
						colorDark: "#000000",
444
						colorLight: "#ffffff",
445
					});
446
				}', true);
447
		}
448
		else
449
		{
450
			$fields = self::getFields('account');
451
		}
452
		setupProfileContext($fields['fields'], $fields['hook']);
453
	}
454
455
456
	/**
457
	 * Allow the user to change the forum options in their profile.
458
	 */
459
	public function action_forumProfile()
460
	{
461
		global $context, $txt;
462
463
		loadTemplate('ProfileOptions');
464
		$this->loadThemeOptions();
465
466
		if (allowedTo(array('profile_extra_own', 'profile_extra_any')))
467
			loadCustomFields($this->_memID, 'forumprofile');
468
469
		$context['sub_template'] = 'edit_options';
470
		$context['page_desc'] = replaceBasicActionUrl($txt['forumProfile_info']);
471
		$context['show_preview_button'] = true;
472
473
		$fields = self::getFields('forumprofile');
474
		setupProfileContext($fields['fields'], $fields['hook']);
475
	}
476
477
	/**
478
	 * Allow the edit of *someone else's* personal message settings.
479
	 */
480
	public function action_pmprefs()
481
	{
482
		global $context, $txt;
483
484
		$this->loadThemeOptions();
485
		loadCustomFields($this->_memID, 'pmprefs');
486
		loadTemplate('ProfileOptions');
487
488
		$context['sub_template'] = 'edit_options';
489
		$context['page_desc'] = $txt['pm_settings_desc'];
490
491
		// Setup the profile context and call the 'integrate_pmprefs_profile_fields' hook
492
		$fields = self::getFields('contactprefs');
493
		setupProfileContext($fields['fields'], $fields['hook']);
494
	}
495
496
	/**
497
	 * Allow the user to pick a theme.
498
	 *
499
	 */
500
	public function action_themepick()
501
	{
502
		global $txt, $context;
503
504
		$this->loadThemeOptions();
505
506
		if (allowedTo(array('profile_extra_own', 'profile_extra_any')))
507
			loadCustomFields($this->_memID, 'theme');
508
509
		loadTemplate('ProfileOptions');
510
511
		$context['sub_template'] = 'edit_options';
512
		$context['page_desc'] = $txt['theme_info'];
513
514
		$fields = self::getFields('theme');
515
		setupProfileContext($fields['fields'], $fields['hook']);
516
	}
517
518
	/**
519
	 * Changing authentication method?
520
	 * Only appropriate for people using OpenID.
521
	 *
522
	 * @param bool $saving = false
523
	 * @throws Elk_Exception
524
	 */
525
	public function action_authentication($saving = false)
526
	{
527
		global $context, $cur_profile, $post_errors, $modSettings;
528
529
		loadLanguage('Login');
530
		loadTemplate('ProfileOptions');
531
532
		// We are saving?
533
		if ($saving)
534
		{
535
			// Moving to password passed authentication?
536
			if ($this->_req->post->authenticate === 'passwd')
537
			{
538
				// Didn't enter anything?
539
				if ($this->_req->post->passwrd1 === '')
540
					$post_errors[] = 'no_password';
541
				// Do the two entries for the password even match?
542
				elseif (!isset($this->_req->post->passwrd2) || $this->_req->post->passwrd1 != $this->_req->post->passwrd2)
543
					$post_errors[] = 'bad_new_password';
544
				// Is it valid?
545
				else
546
				{
547
					require_once(SUBSDIR . '/Auth.subs.php');
548
					$passwordErrors = validatePassword($this->_req->post->passwrd1, $cur_profile['member_name'], array($cur_profile['real_name'], $cur_profile['email_address']));
549
550
					// Were there errors?
551
					if ($passwordErrors !== null)
552
						$post_errors[] = 'password_' . $passwordErrors;
553
				}
554
555
				if (empty($post_errors))
556
				{
557
					// Integration?
558
					call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $this->_req->post->passwrd1));
559
560
					// Go then.
561
					require_once(SUBSDIR . '/Auth.subs.php');
562
					$new_pass = $this->_req->post->passwrd1;
563
					$passwd = validateLoginPassword($new_pass, '', $cur_profile['member_name'], true);
564
565
					// Do the important bits.
566
					require_once(SUBSDIR . '/Members.subs.php');
567
					updateMemberData($this->_memID, array('openid_uri' => '', 'passwd' => $passwd));
568
					if ($context['user']['is_owner'])
569
					{
570
						setLoginCookie(60 * $modSettings['cookieTime'], $this->_memID, hash('sha256', $new_pass . $cur_profile['password_salt']));
571
						redirectexit('action=profile;area=authentication;updated');
572
					}
573
					else
574
						redirectexit('action=profile;u=' . $this->_memID);
575
				}
576
577
				return true;
578
			}
579
			// Not right yet!
580
			elseif ($this->_req->post->authenticate === 'openid' && !empty($this->_req->post->openid_identifier))
581
			{
582
				require_once(SUBSDIR . '/OpenID.subs.php');
583
				require_once(SUBSDIR . '/Members.subs.php');
584
585
				$openID = new OpenID();
586
				$this->_req->post->openid_identifier = $openID->canonize($this->_req->post->openid_identifier);
587
588
				if (memberExists($this->_req->post->openid_identifier))
589
					$post_errors[] = 'openid_in_use';
590
				elseif (empty($post_errors))
591
				{
592
					// Authenticate using the new OpenID URI first to make sure they didn't make a mistake.
593
					if ($context['user']['is_owner'])
594
					{
595
						$_SESSION['new_openid_uri'] = $this->_req->post->openid_identifier;
596
						$openID->validate($this->_req->post->openid_identifier, false, null, 'change_uri');
597
					}
598
					else
599
						updateMemberData($this->_memID, array('openid_uri' => $this->_req->post->openid_identifier));
600
				}
601
			}
602
		}
603
604
		// Some stuff for the template
605
		$context['member']['openid_uri'] = $cur_profile['openid_uri'];
606
		$context['auth_method'] = empty($cur_profile['openid_uri']) ? 'password' : 'openid';
607
		$context['sub_template'] = 'authentication_method';
608
		loadJavascriptFile('register.js');
609
	}
610
611
	/**
612
	 * Display the notifications and settings for changes.
613
	 */
614
	public function action_notification()
615
	{
616
		global $txt, $scripturl, $user_profile, $context, $modSettings;
617
618
		loadTemplate('ProfileOptions');
619
620
		// Going to need this for the list.
621
		require_once(SUBSDIR . '/Boards.subs.php');
622
		require_once(SUBSDIR . '/Topic.subs.php');
623
		require_once(SUBSDIR . '/Profile.subs.php');
624
625
		$context['mention_types'] = getMemberNotificationsProfile($this->_memID);
626
627
		// Fine, start with the board list.
628
		$listOptions = array(
629
			'id' => 'board_notification_list',
630
			'width' => '100%',
631
			'no_items_label' => $txt['notifications_boards_none'] . '<br /><br />' . $txt['notifications_boards_howto'],
632
			'no_items_align' => 'left',
633
			'base_href' => $scripturl . '?action=profile;u=' . $this->_memID . ';area=notification',
634
			'default_sort_col' => 'board_name',
635
			'get_items' => array(
636
				'function' => array($this, 'list_getBoardNotifications'),
637
				'params' => array(
638
					$this->_memID,
639
				),
640
			),
641
			'columns' => array(
642
				'board_name' => array(
643
					'header' => array(
644
						'value' => $txt['notifications_boards'],
645
						'class' => 'lefttext',
646
					),
647
					'data' => array(
648
						'function' => function ($board) {
649
							global $txt;
650
651
							$link = $board['link'];
652
653
							if ($board['new'])
654
								$link .= ' <a href="' . $board['href'] . '"><span class="new_posts">' . $txt['new'] . '</span></a>';
655
656
							return $link;
657
						},
658
					),
659
					'sort' => array(
660
						'default' => 'name',
661
						'reverse' => 'name DESC',
662
					),
663
				),
664
				'delete' => array(
665
					'header' => array(
666
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" />',
667
						'class' => 'centertext',
668
						'style' => 'width:4%;',
669
					),
670
					'data' => array(
671
						'sprintf' => array(
672
							'format' => '<input type="checkbox" name="notify_boards[]" value="%1$d" %2$s />',
673
							'params' => array(
674
								'id' => false,
675
								'checked' => false,
676
							),
677
						),
678
						'class' => 'centertext',
679
					),
680
				),
681
			),
682
			'form' => array(
683
				'href' => $scripturl . '?action=profile;area=notification',
684
				'include_sort' => true,
685
				'include_start' => true,
686
				'hidden_fields' => array(
687
					'u' => $this->_memID,
688
					'sa' => $context['menu_item_selected'],
689
					$context['session_var'] => $context['session_id'],
690
				),
691
				'token' => $context['token_check'],
692
			),
693
			'additional_rows' => array(
694
				array(
695
					'class' => 'submitbutton',
696
					'position' => 'bottom_of_list',
697
					'value' => '
698
						<input type="submit" name="edit_notify_boards" value="' . $txt['notifications_boards_update'] . '" />
699
						<input type="hidden" name="save" value="save" />',
700
				),
701
				array(
702
					'position' => 'after_title',
703
					'value' => getBoardNotificationsCount($this->_memID) == 0 ? $txt['notifications_boards_none'] . '<br />' . $txt['notifications_boards_howto'] : $txt['notifications_boards_current'],
704
				),
705
			),
706
		);
707
708
		// Create the board notification list.
709
		createList($listOptions);
710
711
		// Now do the topic notifications.
712
		$listOptions = array(
713
			'id' => 'topic_notification_list',
714
			'width' => '100%',
715
			'items_per_page' => $modSettings['defaultMaxMessages'],
716
			'no_items_label' => $txt['notifications_topics_none'] . '<br /><br />' . $txt['notifications_topics_howto'],
717
			'no_items_align' => 'left',
718
			'base_href' => $scripturl . '?action=profile;u=' . $this->_memID . ';area=notification',
719
			'default_sort_col' => 'last_post',
720
			'get_items' => array(
721
				'function' => array($this, 'list_getTopicNotifications'),
722
				'params' => array(
723
					$this->_memID,
724
				),
725
			),
726
			'get_count' => array(
727
				'function' => array($this, 'list_getTopicNotificationCount'),
728
				'params' => array(
729
					$this->_memID,
730
				),
731
			),
732
			'columns' => array(
733
				'subject' => array(
734
					'header' => array(
735
						'value' => $txt['notifications_topics'],
736
						'class' => 'lefttext',
737
					),
738
					'data' => array(
739
						'function' => function ($topic) {
740
							global $txt;
741
742
							$link = $topic['link'];
743
744
							if ($topic['new'])
745
								$link .= ' <a href="' . $topic['new_href'] . '"><span class="new_posts">' . $txt['new'] . '</span></a>';
746
747
							$link .= '<br /><span class="smalltext"><em>' . $txt['in'] . ' ' . $topic['board_link'] . '</em></span>';
748
749
							return $link;
750
						},
751
					),
752
					'sort' => array(
753
						'default' => 'ms.subject',
754
						'reverse' => 'ms.subject DESC',
755
					),
756
				),
757
				'started_by' => array(
758
					'header' => array(
759
						'value' => $txt['started_by'],
760
						'class' => 'lefttext',
761
					),
762
					'data' => array(
763
						'db' => 'poster_link',
764
					),
765
					'sort' => array(
766
						'default' => 'real_name_col',
767
						'reverse' => 'real_name_col DESC',
768
					),
769
				),
770
				'last_post' => array(
771
					'header' => array(
772
						'value' => $txt['last_post'],
773
							'class' => 'lefttext',
774
					),
775
					'data' => array(
776
						'sprintf' => array(
777
							'format' => '<span class="smalltext">%1$s<br />' . $txt['by'] . ' %2$s</span>',
778
							'params' => array(
779
								'updated' => false,
780
								'poster_updated_link' => false,
781
							),
782
						),
783
					),
784
					'sort' => array(
785
						'default' => 'ml.id_msg DESC',
786
						'reverse' => 'ml.id_msg',
787
					),
788
				),
789
				'delete' => array(
790
					'header' => array(
791
						'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" />',
792
						'class' => 'centertext',
793
						'style' => 'width:4%;',
794
					),
795
					'data' => array(
796
						'sprintf' => array(
797
							'format' => '<input type="checkbox" name="notify_topics[]" value="%1$d" />',
798
							'params' => array(
799
								'id' => false,
800
							),
801
						),
802
						'class' => 'centertext',
803
					),
804
				),
805
			),
806
			'form' => array(
807
				'href' => $scripturl . '?action=profile;area=notification',
808
				'include_sort' => true,
809
				'include_start' => true,
810
				'hidden_fields' => array(
811
					'u' => $this->_memID,
812
					'sa' => $context['menu_item_selected'],
813
					$context['session_var'] => $context['session_id'],
814
				),
815
				'token' => $context['token_check'],
816
			),
817
			'additional_rows' => array(
818
				array(
819
					'class' => 'submitbutton',
820
					'position' => 'bottom_of_list',
821
					'value' => '
822
						<input type="submit" name="edit_notify_topics" value="' . $txt['notifications_update'] . '" />
823
						<input type="hidden" name="save" value="save" />',
824
				),
825
			),
826
		);
827
828
		// Create the notification list.
829
		createList($listOptions);
830
831
		// What options are set?
832
		$context['member'] += array(
833
			'notify_announcements' => $user_profile[$this->_memID]['notify_announcements'],
834
			'notify_send_body' => $user_profile[$this->_memID]['notify_send_body'],
835
			'notify_types' => $user_profile[$this->_memID]['notify_types'],
836
			'notify_regularity' => $user_profile[$this->_memID]['notify_regularity'],
837
		);
838
839
		$this->loadThemeOptions();
840
	}
841
842
	/**
843
	 * Callback for createList() in action_notification()
844
	 *
845
	 * - Retrieve topic notifications count.
846
	 *
847
	 * @param int $memID id_member the id of the member who's notifications we are loading
848
	 * @return integer
849
	 */
850
	public function list_getTopicNotificationCount($memID)
851
	{
852
		// Topic notifications count, for the list
853
		return topicNotificationCount($memID);
854
	}
855
856
	/**
857
	 * Callback for createList() in action_notification()
858
	 *
859
	 * @param int $start The item to start with (for pagination purposes)
860
	 * @param int $items_per_page  The number of items to show per page
861
	 * @param string $sort A string indicating how to sort the results
862
	 * @param int $memID id_member
863
	 * @return mixed array of topic notifications
864
	 */
865
	public function list_getTopicNotifications($start, $items_per_page, $sort, $memID)
866
	{
867
		// Topic notifications, for the list
868
		return topicNotifications($start, $items_per_page, $sort, $memID);
869
	}
870
871
	/**
872
	 * Callback for createList() in action_notification()
873
	 *
874
	 * @uses template_ignoreboards()
875
	 * @param int $start The item to start with (for pagination purposes)
876
	 * @param int $items_per_page  The number of items to show per page
877
	 * @param string $sort A string indicating how to sort the results
878
	 * @param int $memID id_member
879
	 * @return mixed[] array of board notifications
880
	 */
881
	public function list_getBoardNotifications($start, $items_per_page, $sort, $memID)
882
	{
883
		// Return boards you see and their notification status for the list
884
		return boardNotifications($start, $items_per_page, $sort, $memID);
885
	}
886
887
	/**
888
	 * Allows the user to see the list of their ignored boards.
889
	 * (and un-ignore them)
890
	 */
891
	public function action_ignoreboards()
892
	{
893
		global $context, $modSettings, $cur_profile;
894
895
		// Have the admins enabled this option?
896
		if (empty($modSettings['allow_ignore_boards']))
897
			throw new Elk_Exception('ignoreboards_disallowed', 'user');
898
899
		loadTemplate('ProfileOptions');
900
901
		$context['sub_template'] = 'ignoreboards';
902
		require_once(SUBSDIR . '/Boards.subs.php');
903
		$context += getBoardList(array('not_redirection' => true, 'ignore' => !empty($cur_profile['ignore_boards']) ? explode(',', $cur_profile['ignore_boards']) : array()));
904
905
		// Include a list of boards per category for easy toggling.
906 View Code Duplication
		foreach ($context['categories'] as $cat => &$category)
907
		{
908
			$context['boards_in_category'][$cat] = count($category['boards']);
909
			$category['child_ids'] = array_keys($category['boards']);
910
		}
911
912
		$this->loadThemeOptions();
913
	}
914
915
	/**
916
	 * Function to allow the user to choose group membership etc...
917
	 */
918
	public function action_groupMembership()
919
	{
920
		global $txt, $user_profile, $context;
921
922
		loadTemplate('ProfileOptions');
923
		$context['sub_template'] = 'groupMembership';
924
925
		$curMember = $user_profile[$this->_memID];
926
		$context['primary_group'] = $curMember['id_group'];
927
928
		// Can they manage groups?
929
		$context['can_manage_membergroups'] = allowedTo('manage_membergroups');
930
		$context['can_manage_protected'] = allowedTo('admin_forum');
931
		$context['can_edit_primary'] = $context['can_manage_protected'];
932
		$context['update_message'] = isset($this->_req->query->msg) && isset($txt['group_membership_msg_' . $this->_req->query->msg]) ? $txt['group_membership_msg_' . $this->_req->query->msg] : '';
933
934
		// Get all the groups this user is a member of.
935
		$groups = explode(',', $curMember['additional_groups']);
936
		$groups[] = $curMember['id_group'];
937
938
		// Ensure the query doesn't croak!
939
		if (empty($groups))
940
			$groups = array(0);
941
942
		// Just to be sure...
943
		$groups = array_map('intval', $groups);
944
945
		// Get all the membergroups they can join.
946
		require_once(SUBSDIR . '/ProfileOptions.subs.php');
947
		$context['groups'] = loadMembergroupsJoin($groups, $this->_memID);
948
949
		// Add registered members on the end.
950
		$context['groups']['member'][0] = array(
951
			'id' => 0,
952
			'name' => $txt['regular_members'],
953
			'desc' => $txt['regular_members_desc'],
954
			'type' => 0,
955
			'is_primary' => $context['primary_group'] == 0 ? true : false,
956
			'can_be_primary' => true,
957
			'can_leave' => 0,
958
		);
959
960
		// No changing primary one unless you have enough groups!
961
		if (count($context['groups']['member']) < 2)
962
			$context['can_edit_primary'] = false;
963
964
		// In the special case that someone is requesting membership of a group, setup some special context vars.
965
		if (isset($this->_req->query->request)
966
			&& isset($context['groups']['available'][(int) $this->_req->query->request])
967
			&& $context['groups']['available'][(int) $this->_req->query->request]['type'] == 2)
968
		{
969
			$context['group_request'] = $context['groups']['available'][(int) $this->_req->query->request];
970
		}
971
	}
972
973
	/**
974
	 * This function actually makes all the group changes
975
	 *
976
	 * @return string
977
	 * @throws Elk_Exception no_access
978
	 */
979
	public function action_groupMembership2()
980
	{
981
		global $context, $user_profile, $modSettings, $scripturl, $language;
982
983
		// Let's be extra cautious...
984
		if (!$context['user']['is_owner'] || empty($modSettings['show_group_membership']))
985
			isAllowedTo('manage_membergroups');
986
987
		$group_id = $this->_req->getPost('gid', 'intval', $this->_req->getQuery('gid', 'intval', null));
988
989
		if (!isset($group_id) && !isset($this->_req->post->primary))
990
			throw new Elk_Exception('no_access', false);
991
992
		// GID may be from a link or a form
993
		checkSession(isset($this->_req->query->gid) ? 'get' : 'post');
994
995
		require_once(SUBSDIR . '/Membergroups.subs.php');
996
997
		$old_profile = &$user_profile[$this->_memID];
998
		$context['can_manage_membergroups'] = allowedTo('manage_membergroups');
999
		$context['can_manage_protected'] = allowedTo('admin_forum');
1000
1001
		// By default the new primary is the old one.
1002
		$newPrimary = $old_profile['id_group'];
1003
		$addGroups = array_flip(explode(',', $old_profile['additional_groups']));
1004
		$canChangePrimary = $old_profile['id_group'] == 0;
1005
		$changeType = isset($this->_req->post->primary) ? 'primary' : (isset($this->_req->post->req) ? 'request' : 'free');
1006
1007
		// One way or another, we have a target group in mind...
1008
		$group_id = isset($group_id) ? $group_id : (int) $this->_req->post->primary;
1009
		$foundTarget = $changeType === 'primary' && $group_id == 0 ? true : false;
1010
1011
		// Sanity check!!
1012
		if ($group_id == 1)
1013
			isAllowedTo('admin_forum');
1014
1015
		// What ever we are doing, we need to determine if changing primary is possible!
1016
		$groups_details = membergroupsById(array($group_id, $old_profile['id_group']), 0, true);
1017
1018
		// Protected groups require proper permissions!
1019
		if ($group_id != 1 && $groups_details[$group_id]['group_type'] == 1)
1020
			isAllowedTo('admin_forum');
1021
1022
		foreach ($groups_details as $key => $row)
1023
		{
1024
			// Is this the new group?
1025
			if ($row['id_group'] == $group_id)
1026
			{
1027
				$foundTarget = true;
1028
				$group_name = $row['group_name'];
1029
1030
				// Does the group type match what we're doing - are we trying to request a non-requestable group?
1031
				if ($changeType === 'request' && $row['group_type'] != 2)
1032
					throw new Elk_Exception('no_access', false);
1033
				// What about leaving a requestable group we are not a member of?
1034
				elseif ($changeType === 'free' && $row['group_type'] == 2 && $old_profile['id_group'] != $row['id_group'] && !isset($addGroups[$row['id_group']]))
1035
					throw new Elk_Exception('no_access', false);
1036
				elseif ($changeType === 'free' && $row['group_type'] != 3 && $row['group_type'] != 2)
1037
					throw new Elk_Exception('no_access', false);
1038
1039
				// We can't change the primary group if this is hidden!
1040
				if ($row['hidden'] == 2)
1041
					$canChangePrimary = false;
1042
			}
1043
1044
			// If this is their old primary, can we change it?
1045
			if ($row['id_group'] == $old_profile['id_group'] && ($row['group_type'] > 1 || $context['can_manage_membergroups']) && $canChangePrimary !== false)
1046
				$canChangePrimary = true;
1047
1048
			// If we are not doing a force primary move, don't do it automatically if current primary is not 0.
1049
			if ($changeType != 'primary' && $old_profile['id_group'] != 0)
1050
				$canChangePrimary = false;
1051
1052
			// If this is the one we are acting on, can we even act?
1053
			if ((!$context['can_manage_protected'] && $row['group_type'] == 1) || (!$context['can_manage_membergroups'] && $row['group_type'] == 0))
1054
				$canChangePrimary = false;
1055
		}
1056
1057
		// Didn't find the target?
1058
		if (!$foundTarget)
1059
			throw new Elk_Exception('no_access', false);
1060
1061
		// Final security check, don't allow users to promote themselves to admin.
1062
		require_once(SUBSDIR . '/ProfileOptions.subs.php');
1063
		if ($context['can_manage_membergroups'] && !allowedTo('admin_forum'))
1064
		{
1065
			$disallow = checkMembergroupChange($group_id);
1066
			if ($disallow)
1067
				isAllowedTo('admin_forum');
1068
		}
1069
1070
		// If we're requesting, add the note then return.
1071
		if ($changeType === 'request')
1072
		{
1073
			if (logMembergroupRequest($group_id, $this->_memID))
1074
				throw new Elk_Exception('profile_error_already_requested_group');
1075
1076
			// Send an email to all group moderators etc.
1077
			require_once(SUBSDIR . '/Mail.subs.php');
1078
1079
			// Do we have any group moderators?
1080
			require_once(SUBSDIR . '/Membergroups.subs.php');
1081
			$moderators = array_keys(getGroupModerators($group_id));
1082
1083
			// Otherwise this is the backup!
1084
			if (empty($moderators))
1085
			{
1086
				require_once(SUBSDIR . '/Members.subs.php');
1087
				$moderators = membersAllowedTo('manage_membergroups');
1088
			}
1089
1090
			if (!empty($moderators))
1091
			{
1092
				require_once(SUBSDIR . '/Members.subs.php');
1093
				$members = getBasicMemberData($moderators, array('preferences' => true, 'sort' => 'lngfile'));
1094
1095
				foreach ($members as $member)
1096
				{
1097
					if ($member['notify_types'] != 4)
1098
						continue;
1099
1100
					// Check whether they are interested.
1101 View Code Duplication
					if (!empty($member['mod_prefs']))
1102
					{
1103
						list (,, $pref_binary) = explode('|', $member['mod_prefs']);
1104
						if (!($pref_binary & 4))
1105
							continue;
1106
					}
1107
1108
					$replacements = array(
1109
						'RECPNAME' => $member['member_name'],
1110
						'APPYNAME' => $old_profile['member_name'],
1111
						'GROUPNAME' => $group_name,
0 ignored issues
show
Bug introduced by
The variable $group_name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1112
						'REASON' => $this->_req->post->reason,
1113
						'MODLINK' => $scripturl . '?action=moderate;area=groups;sa=requests',
1114
					);
1115
1116
					$emaildata = loadEmailTemplate('request_membership', $replacements, empty($member['lngfile']) || empty($modSettings['userLanguage']) ? $language : $member['lngfile']);
1117
					sendmail($member['email_address'], $emaildata['subject'], $emaildata['body'], null, null, false, 2);
1118
				}
1119
			}
1120
1121
			return $changeType;
1122
		}
1123
		// Otherwise we are leaving/joining a group.
1124
		elseif ($changeType === 'free')
1125
		{
1126
			// Are we leaving?
1127
			if ($old_profile['id_group'] == $group_id || isset($addGroups[$group_id]))
1128
			{
1129
				if ($old_profile['id_group'] == $group_id)
1130
					$newPrimary = 0;
1131
				else
1132
					unset($addGroups[$group_id]);
1133
			}
1134
			// ... if not, must be joining.
1135
			else
1136
			{
1137
				// Can we change the primary, and do we want to?
1138
				if ($canChangePrimary)
1139
				{
1140
					if ($old_profile['id_group'] != 0)
1141
						$addGroups[$old_profile['id_group']] = -1;
1142
					$newPrimary = $group_id;
1143
				}
1144
				// Otherwise it's an additional group...
1145
				else
1146
					$addGroups[$group_id] = -1;
1147
			}
1148
		}
1149
		// Finally, we must be setting the primary.
1150
		elseif ($canChangePrimary)
1151
		{
1152
			if ($old_profile['id_group'] != 0)
1153
				$addGroups[$old_profile['id_group']] = -1;
1154
			if (isset($addGroups[$group_id]))
1155
				unset($addGroups[$group_id]);
1156
			$newPrimary = $group_id;
1157
		}
1158
1159
		// Finally, we can make the changes!
1160
		foreach ($addGroups as $id => $dummy)
1161
		{
1162
			if (empty($id))
1163
				unset($addGroups[$id]);
1164
		}
1165
		$addGroups = implode(',', array_flip($addGroups));
1166
1167
		// Ensure that we don't cache permissions if the group is changing.
1168 View Code Duplication
		if ($context['user']['is_owner'])
1169
			$_SESSION['mc']['time'] = 0;
1170
		else
1171
			updateSettings(array('settings_updated' => time()));
1172
1173
		require_once(SUBSDIR . '/Members.subs.php');
1174
		updateMemberData($this->_memID, array('id_group' => $newPrimary, 'additional_groups' => $addGroups));
1175
1176
		return $changeType;
1177
	}
1178
1179
	/**
1180
	 * Load the options for an user.
1181
	 */
1182
	public function loadThemeOptions()
1183
	{
1184
		global $context, $options, $cur_profile;
1185
1186 View Code Duplication
		if (isset($this->_req->post->default_options))
1187
			$this->_req->post->options = isset($this->_req->post->options) ? $this->_req->post->options + $this->_req->post->default_options : $this->_req->post->default_options;
1188
1189
		if ($context['user']['is_owner'])
1190
		{
1191
			$context['member']['options'] = $options;
1192
1193
			if (isset($this->_req->post->options) && is_array($this->_req->post->options))
1194
			{
1195
				foreach ($this->_req->post->options as $k => $v)
1196
					$context['member']['options'][$k] = $v;
1197
			}
1198
		}
1199
		else
1200
		{
1201
			require_once(SUBSDIR . '/Themes.subs.php');
1202
			$context['member']['options'] = loadThemeOptionsInto(array(1, (int) $cur_profile['id_theme']), array(-1, $this->_memID), $context['member']['options']);
1203
1204
			if (isset($this->_req->post->options))
1205
			{
1206
				foreach ($this->_req->post->options as $var => $val)
1207
					$context['member']['options'][$var] = $val;
1208
			}
1209
		}
1210
	}
1211
}
1212