Completed
Push — release-2.1 ( 4c82a0...64d581 )
by Rick
09:29
created

ManageSettings.php ➔ ModifyAntispamSettings()   F

Complexity

Conditions 52
Paths > 20000

Size

Total Lines 304
Code Lines 152

Duplication

Lines 12
Ratio 3.95 %

Importance

Changes 0
Metric Value
cc 52
eloc 152
nc 149762
nop 1
dl 12
loc 304
rs 2
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 is here to make it easier for installed mods to have
5
 * settings and options.
6
 *
7
 * Simple Machines Forum (SMF)
8
 *
9
 * @package SMF
10
 * @author Simple Machines http://www.simplemachines.org
11
 * @copyright 2016 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 3
15
 */
16
17
if (!defined('SMF'))
18
	die('No direct access...');
19
20
/**
21
 * This function makes sure the requested subaction does exists, if it doesn't, it sets a default action or.
22
 *
23
 * @param array $subActions An array containing all possible subactions.
24
 * @param string $defaultAction The default action to be called if no valid subaction was found.
25
 */
26
function loadGeneralSettingParameters($subActions = array(), $defaultAction = '')
27
{
28
	global $context, $sourcedir;
29
30
	// You need to be an admin to edit settings!
31
	isAllowedTo('admin_forum');
32
33
	loadLanguage('Help');
34
	loadLanguage('ManageSettings');
35
36
	// Will need the utility functions from here.
37
	require_once($sourcedir . '/ManageServer.php');
38
39
	$context['sub_template'] = 'show_settings';
40
41
	// By default do the basic settings.
42
	$_REQUEST['sa'] = isset($_REQUEST['sa']) && isset($subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : (!empty($defaultAction) ? $defaultAction : array_pop($temp = array_keys($subActions)));
0 ignored issues
show
Bug introduced by
$temp = array_keys($subActions) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
43
	$context['sub_action'] = $_REQUEST['sa'];
44
}
45
46
/**
47
 * This function passes control through to the relevant tab.
48
 */
49
function ModifyFeatureSettings()
50
{
51
	global $context, $txt, $settings;
52
53
	$context['page_title'] = $txt['modSettings_title'];
54
55
	$subActions = array(
56
		'basic' => 'ModifyBasicSettings',
57
		'bbc' => 'ModifyBBCSettings',
58
		'layout' => 'ModifyLayoutSettings',
59
		'sig' => 'ModifySignatureSettings',
60
		'profile' => 'ShowCustomProfiles',
61
		'profileedit' => 'EditCustomProfiles',
62
		'likes' => 'ModifyLikesSettings',
63
		'mentions' => 'ModifyMentionsSettings',
64
		'alerts' => 'ModifyAlertsSettings',
65
	);
66
67
	loadGeneralSettingParameters($subActions, 'basic');
68
69
	// Load up all the tabs...
70
	$context[$context['admin_menu_name']]['tab_data'] = array(
71
		'title' => $txt['modSettings_title'],
72
		'help' => 'featuresettings',
73
		'description' => sprintf($txt['modSettings_desc'], $settings['theme_id'], $context['session_id'], $context['session_var']),
74
		'tabs' => array(
75
			'basic' => array(
76
			),
77
			'bbc' => array(
78
				'description' => $txt['manageposts_bbc_settings_description'],
79
			),
80
			'layout' => array(
81
			),
82
			'sig' => array(
83
				'description' => $txt['signature_settings_desc'],
84
			),
85
			'profile' => array(
86
				'description' => $txt['custom_profile_desc'],
87
			),
88
			'likes' => array(
89
			),
90
			'mentions' => array(
91
			),
92
			'alerts' => array(
93
				'description' => $txt['notifications_desc'],
94
			),
95
		),
96
	);
97
98
	call_integration_hook('integrate_modify_features', array(&$subActions));
99
100
	// Call the right function for this sub-action.
101
	call_helper($subActions[$_REQUEST['sa']]);
102
}
103
104
/**
105
 * This my friend, is for all the mod authors out there.
106
 */
107
function ModifyModSettings()
108
{
109
	global $context, $txt;
110
111
	$context['page_title'] = $txt['admin_modifications'];
112
113
	$subActions = array(
114
		'general' => 'ModifyGeneralModSettings',
115
		// Mod authors, once again, if you have a whole section to add do it AFTER this line, and keep a comma at the end.
116
	);
117
118
	// Make it easier for mods to add new areas.
119
	call_integration_hook('integrate_modify_modifications', array(&$subActions));
120
121
	loadGeneralSettingParameters($subActions, 'general');
122
123
	// Load up all the tabs...
124
	$context[$context['admin_menu_name']]['tab_data'] = array(
125
		'title' => $txt['admin_modifications'],
126
		'help' => 'modsettings',
127
		'description' => $txt['modification_settings_desc'],
128
		'tabs' => array(
129
			'general' => array(
130
			),
131
		),
132
	);
133
134
	// Call the right function for this sub-action.
135
	call_helper($subActions[$_REQUEST['sa']]);
136
}
137
138
/**
139
 * Config array for changing the basic forum settings
140
 * Accessed  from ?action=admin;area=featuresettings;sa=basic;
141
 *
142
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
143
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
144
 */
145
function ModifyBasicSettings($return_config = false)
146
{
147
	global $txt, $scripturl, $context, $modSettings;
148
149
	// We need to know if personal text is enabled, and if it's in the registration fields option.
150
	// If admins have set it up as an on-registration thing, they can't set a default value (because it'll never be used)
151
	$disabled_fields = isset($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
152
	$reg_fields = isset($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array();
153
	$can_personal_text = !in_array('personal_text', $disabled_fields) && !in_array('personal_text', $reg_fields);
154
155
	$config_vars = array(
156
			// Big Options... polls, sticky, bbc....
157
			array('select', 'pollMode', array($txt['disable_polls'], $txt['enable_polls'], $txt['polls_as_topics'])),
158
		'',
159
			// Basic stuff, titles, flash, permissions...
160
			array('check', 'allow_guestAccess'),
161
			array('check', 'enable_buddylist'),
162
			array('check', 'allow_hideOnline'),
163
			array('check', 'titlesEnable'),
164
			array('text', 'default_personal_text', 'subtext' => $txt['default_personal_text_note'], 'disabled' => !$can_personal_text),
165
			array('check', 'topic_move_any'),
166
			array('int', 'defaultMaxListItems', 'step' => 1, 'min' => 1, 'max' => 999),
167
		'',
168
			// Jquery source
169
			array('select', 'jquery_source', array('auto' => $txt['jquery_auto'], 'local' => $txt['jquery_local'], 'cdn' => $txt['jquery_cdn'], 'custom' => $txt['jquery_custom']), 'onchange' => 'if (this.value == \'custom\'){document.getElementById(\'jquery_custom\').disabled = false; } else {document.getElementById(\'jquery_custom\').disabled = true;}'),
170
			array('text', 'jquery_custom', 'disabled' => isset($modSettings['jquery_source']) && $modSettings['jquery_source'] != 'custom', 'size' => 75),
171
		'',
172
			// css and js minification.
173
			array('check', 'minimize_files'),
174
		'',
175
			// SEO stuff
176
			array('check', 'queryless_urls', 'subtext' => '<strong>' . $txt['queryless_urls_note'] . '</strong>'),
177
			array('text', 'meta_keywords', 'subtext' => $txt['meta_keywords_note'], 'size' => 50),
178
		'',
179
			// Number formatting, timezones.
180
			array('text', 'time_format'),
181
			array('float', 'time_offset', 'subtext' => $txt['setting_time_offset_note'], 6, 'postinput' => $txt['hours'], 'step' => 0.25, 'min' => -23.5, 'max' => 23.5),
182
			'default_timezone' => array('select', 'default_timezone', array()),
183
		'',
184
			// Who's online?
185
			array('check', 'who_enabled'),
186
			array('int', 'lastActive', 6, 'postinput' => $txt['minutes']),
187
		'',
188
			// Statistics.
189
			array('check', 'trackStats'),
190
			array('check', 'hitStats'),
191
		'',
192
			// Option-ish things... miscellaneous sorta.
193
			array('check', 'allow_disableAnnounce'),
194
			array('check', 'disallow_sendBody'),
195
		'',
196
			// Alerts stuff
197
			array('check', 'enable_ajax_alerts'),
198
	);
199
200
	// Get all the time zones.
201
	if (function_exists('timezone_identifiers_list') && function_exists('date_default_timezone_set'))
202
	{
203
		$all_zones = timezone_identifiers_list();
204
		// Make sure we set the value to the same as the printed value.
205
		foreach ($all_zones as $zone)
206
			$config_vars['default_timezone'][2][$zone] = $zone;
207
	}
208
	else
209
		unset($config_vars['default_timezone']);
210
211
	call_integration_hook('integrate_modify_basic_settings', array(&$config_vars));
212
213
	if ($return_config)
214
		return $config_vars;
215
216
	// Saving?
217
	if (isset($_GET['save']))
218
	{
219
		checkSession();
220
221
		// Prevent absurd boundaries here - make it a day tops.
222
		if (isset($_POST['lastActive']))
223
			$_POST['lastActive'] = min((int) $_POST['lastActive'], 1440);
224
225
		call_integration_hook('integrate_save_basic_settings');
226
227
		saveDBSettings($config_vars);
228
		$_SESSION['adm-save'] = true;
229
230
		writeLog();
231
		redirectexit('action=admin;area=featuresettings;sa=basic');
232
	}
233
234
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=basic';
235
	$context['settings_title'] = $txt['mods_cat_features'];
236
237
	prepareDBSettingContext($config_vars);
238
}
239
240
/**
241
 * Set a few Bulletin Board Code settings. It loads a list of Bulletin Board Code tags to allow disabling tags.
242
 * Requires the admin_forum permission.
243
 * Accessed from ?action=admin;area=featuresettings;sa=bbc.
244
 *
245
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
246
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
247
 * @uses Admin template, edit_bbc_settings sub-template.
248
 */
249
function ModifyBBCSettings($return_config = false)
250
{
251
	global $context, $txt, $modSettings, $scripturl, $sourcedir;
252
253
	$config_vars = array(
254
			// Main tweaks
255
			array('check', 'enableBBC'),
256
			array('check', 'enableBBC', 0, 'onchange' => 'toggleBBCDisabled(\'disabledBBC\', !this.checked);'),
257
			array('check', 'enablePostHTML'),
258
			array('check', 'autoLinkUrls'),
259
		'',
260
			array('bbc', 'disabledBBC'),
261
	);
262
263
	$context['settings_post_javascript'] = '
264
		toggleBBCDisabled(\'disabledBBC\', ' . (empty($modSettings['enableBBC']) ? 'true' : 'false') . ');';
265
266
	call_integration_hook('integrate_modify_bbc_settings', array(&$config_vars));
267
268
	if ($return_config)
269
		return $config_vars;
270
271
	// Setup the template.
272
	require_once($sourcedir . '/ManageServer.php');
273
	$context['sub_template'] = 'show_settings';
274
	$context['page_title'] = $txt['manageposts_bbc_settings_title'];
275
276
	// Make sure we check the right tags!
277
	$modSettings['bbc_disabled_disabledBBC'] = empty($modSettings['disabledBBC']) ? array() : explode(',', $modSettings['disabledBBC']);
278
279
	// Saving?
280
	if (isset($_GET['save']))
281
	{
282
		checkSession();
283
284
		// Clean up the tags.
285
		$bbcTags = array();
286
		foreach (parse_bbc(false) as $tag)
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The expression parse_bbc(false) of type string is not traversable.
Loading history...
287
			$bbcTags[] = $tag['tag'];
288
289 View Code Duplication
		if (!isset($_POST['disabledBBC_enabledTags']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
290
			$_POST['disabledBBC_enabledTags'] = array();
291
		elseif (!is_array($_POST['disabledBBC_enabledTags']))
292
			$_POST['disabledBBC_enabledTags'] = array($_POST['disabledBBC_enabledTags']);
293
		// Work out what is actually disabled!
294
		$_POST['disabledBBC'] = implode(',', array_diff($bbcTags, $_POST['disabledBBC_enabledTags']));
295
296
		call_integration_hook('integrate_save_bbc_settings', array($bbcTags));
297
298
		saveDBSettings($config_vars);
299
		$_SESSION['adm-save'] = true;
300
		redirectexit('action=admin;area=featuresettings;sa=bbc');
301
	}
302
303
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=bbc';
304
	$context['settings_title'] = $txt['manageposts_bbc_settings_title'];
305
306
	prepareDBSettingContext($config_vars);
307
}
308
309
/**
310
 * Allows modifying the global layout settings in the forum
311
 * Accessed through ?action=admin;area=featuresettings;sa=layout;
312
 *
313
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
314
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
315
 */
316
function ModifyLayoutSettings($return_config = false)
317
{
318
	global $txt, $scripturl, $context;
319
320
	$config_vars = array(
321
			// Pagination stuff.
322
			array('check', 'compactTopicPagesEnable'),
323
			array('int', 'compactTopicPagesContiguous', null, $txt['contiguous_page_display'] . '<div class="smalltext">' . str_replace(' ', '&nbsp;', '"3" ' . $txt['to_display'] . ': <strong>1 ... 4 [5] 6 ... 9</strong>') . '<br>' . str_replace(' ', '&nbsp;', '"5" ' . $txt['to_display'] . ': <strong>1 ... 3 4 [5] 6 7 ... 9</strong>') . '</div>'),
324
			array('int', 'defaultMaxMembers'),
325
		'',
326
			// Stuff that just is everywhere - today, search, online, etc.
327
			array('select', 'todayMod', array($txt['today_disabled'], $txt['today_only'], $txt['yesterday_today'])),
328
			array('check', 'onlineEnable'),
329
		'',
330
			// This is like debugging sorta.
331
			array('check', 'timeLoadPageEnable'),
332
	);
333
334
	call_integration_hook('integrate_layout_settings', array(&$config_vars));
335
336
	if ($return_config)
337
		return $config_vars;
338
339
	// Saving?
340 View Code Duplication
	if (isset($_GET['save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
341
	{
342
		checkSession();
343
344
		call_integration_hook('integrate_save_layout_settings');
345
346
		saveDBSettings($config_vars);
347
		$_SESSION['adm-save'] = true;
348
		writeLog();
349
350
		redirectexit('action=admin;area=featuresettings;sa=layout');
351
	}
352
353
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=layout';
354
	$context['settings_title'] = $txt['mods_cat_layout'];
355
356
	prepareDBSettingContext($config_vars);
357
}
358
359
/**
360
 * Config array for changing like settings
361
 * Accessed  from ?action=admin;area=featuresettings;sa=likes;
362
 *
363
 * @param bool $return_config Whether or not to return the config_vars array
364
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
365
 */
366
function ModifyLikesSettings($return_config = false)
367
{
368
	global $txt, $scripturl, $context;
369
370
	$config_vars = array(
371
		array('check', 'enable_likes'),
372
		array('permissions', 'likes_view'),
373
		array('permissions', 'likes_like'),
374
	);
375
376
	call_integration_hook('integrate_likes_settings', array(&$config_vars));
377
378
	if ($return_config)
379
		return $config_vars;
380
381
	// Saving?
382
	if (isset($_GET['save']))
383
	{
384
		checkSession();
385
386
		call_integration_hook('integrate_save_likes_settings');
387
388
		saveDBSettings($config_vars);
389
		$_SESSION['adm-save'] = true;
390
		redirectexit('action=admin;area=featuresettings;sa=likes');
391
	}
392
393
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=likes';
394
	$context['settings_title'] = $txt['likes'];
395
396
	prepareDBSettingContext($config_vars);
397
}
398
399
/**
400
 * Config array for changing like settings
401
 * Accessed  from ?action=admin;area=featuresettings;sa=mentions;
402
 *
403
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
404
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
405
 */
406
function ModifyMentionsSettings($return_config = false)
407
{
408
	global $txt, $scripturl, $context;
409
410
	$config_vars = array(
411
		array('check', 'enable_mentions'),
412
		array('permissions', 'mention'),
413
	);
414
415
	call_integration_hook('integrate_mentions_settings', array(&$config_vars));
416
417
	if ($return_config)
418
		return $config_vars;
419
420
	// Saving?
421
	if (isset($_GET['save']))
422
	{
423
		checkSession();
424
425
		call_integration_hook('integrate_save_mentions_settings');
426
427
		saveDBSettings($config_vars);
428
		$_SESSION['adm-save'] = true;
429
		redirectexit('action=admin;area=featuresettings;sa=mentions');
430
	}
431
432
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=mentions';
433
	$context['settings_title'] = $txt['mentions'];
434
435
	prepareDBSettingContext($config_vars);
436
}
437
438
/**
439
 * Moderation type settings - although there are fewer than we have you believe ;)
440
 *
441
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
442
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
443
 */
444
function ModifyWarningSettings($return_config = false)
445
{
446
	global $txt, $scripturl, $context, $modSettings, $sourcedir;
447
448
	// You need to be an admin to edit settings!
449
	isAllowedTo('admin_forum');
450
451
	loadLanguage('Help');
452
	loadLanguage('ManageSettings');
453
454
	// We need the existing ones for this
455
	list ($currently_enabled, $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
456
457
	$config_vars = array(
458
			// Warning system?
459
			'enable' => array('check', 'warning_enable'),
460
	);
461
462
	if (!empty($modSettings['warning_settings']) && $currently_enabled)
463
		$config_vars += array(
464
			'',
465
				array('int', 'warning_watch', 'subtext' => $txt['setting_warning_watch_note'] . ' ' . $txt['zero_to_disable']),
466
				'moderate' => array('int', 'warning_moderate', 'subtext' => $txt['setting_warning_moderate_note'] . ' ' . $txt['zero_to_disable']),
467
				array('int', 'warning_mute', 'subtext' => $txt['setting_warning_mute_note'] . ' ' . $txt['zero_to_disable']),
468
				'rem1' => array('int', 'user_limit', 'subtext' => $txt['setting_user_limit_note']),
469
				'rem2' => array('int', 'warning_decrement', 'subtext' => $txt['setting_warning_decrement_note'] . ' ' . $txt['zero_to_disable']),
470
				array('permissions', 'view_warning'),
471
		);
472
473
	call_integration_hook('integrate_warning_settings', array(&$config_vars));
474
475
	if ($return_config)
476
		return $config_vars;
477
478
	// Cannot use moderation if post moderation is not enabled.
479
	if (!$modSettings['postmod_active'])
480
		unset($config_vars['moderate']);
481
482
	// Will need the utility functions from here.
483
	require_once($sourcedir . '/ManageServer.php');
484
485
	// Saving?
486
	if (isset($_GET['save']))
487
	{
488
		checkSession();
489
490
		// Make sure these don't have an effect.
491
		if (!$currently_enabled && empty($_POST['warning_enable']))
492
		{
493
			$_POST['warning_watch'] = 0;
494
			$_POST['warning_moderate'] = 0;
495
			$_POST['warning_mute'] = 0;
496
		}
497
		// If it was disabled and we're enabling it now, set some sane defaults.
498
		elseif (!$currently_enabled && !empty($_POST['warning_enable']))
499
		{
500
			// Need to add these, these weren't there before...
501
			$vars = array(
502
				'warning_watch' => 10,
503
				'warning_mute' => 60,
504
			);
505
			if ($modSettings['postmod_active'])
506
				$vars['warning_moderate'] = 35;
507
508
			foreach ($vars as $var => $value)
509
			{
510
				$config_vars[] = array('int', $var);
511
				$_POST[$var] = $value;
512
			}
513
		}
514
		else
515
		{
516
			$_POST['warning_watch'] = min($_POST['warning_watch'], 100);
517
			$_POST['warning_moderate'] = $modSettings['postmod_active'] ? min($_POST['warning_moderate'], 100) : 0;
518
			$_POST['warning_mute'] = min($_POST['warning_mute'], 100);
519
		}
520
521
		// We might not have these already depending on how we got here.
522
		$_POST['user_limit'] = isset($_POST['user_limit']) ? (int) $_POST['user_limit'] : $modSettings['user_limit'];
523
		$_POST['warning_decrement'] = isset($_POST['warning_decrement']) ? (int) $_POST['warning_decrement'] : $modSettings['warning_decrement'];
524
525
		// Fix the warning setting array!
526
		$_POST['warning_settings'] = (!empty($_POST['warning_enable']) ? 1 : 0) . ',' . min(100, $_POST['user_limit']) . ',' . min(100, $_POST['warning_decrement']);
527
		$save_vars = $config_vars;
528
		$save_vars[] = array('text', 'warning_settings');
529
		unset($save_vars['enable'], $save_vars['rem1'], $save_vars['rem2']);
530
531
		call_integration_hook('integrate_save_warning_settings', array(&$save_vars));
532
533
		saveDBSettings($save_vars);
534
		$_SESSION['adm-save'] = true;
535
		redirectexit('action=admin;area=warnings');
536
	}
537
538
	// We actually store lots of these together - for efficiency.
539
	list ($modSettings['warning_enable'], $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
540
541
	$context['sub_template'] = 'show_settings';
542
	$context['post_url'] = $scripturl . '?action=admin;area=warnings;save';
543
	$context['settings_title'] = $txt['warnings'];
544
	$context['page_title'] = $txt['warnings'];
545
546
	$context[$context['admin_menu_name']]['tab_data'] = array(
547
		'title' => $txt['warnings'],
548
		'help' => '',
549
		'description' => $txt['warnings_desc'],
550
	);
551
552
	prepareDBSettingContext($config_vars);
553
}
554
555
/**
556
 * Let's try keep the spam to a minimum ah Thantos?
557
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
558
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
559
 */
560
function ModifyAntispamSettings($return_config = false)
561
{
562
	global $txt, $scripturl, $context, $modSettings, $smcFunc, $language, $sourcedir;
563
564
	loadLanguage('Help');
565
	loadLanguage('ManageSettings');
566
567
	// Generate a sample registration image.
568
	$context['use_graphic_library'] = in_array('gd', get_loaded_extensions());
569
	$context['verification_image_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand());
570
571
	$config_vars = array(
572
				array('check', 'reg_verification'),
573
				array('check', 'search_enable_captcha'),
574
				// This, my friend, is a cheat :p
575
				'guest_verify' => array('check', 'guests_require_captcha', 'subtext' => $txt['setting_guests_require_captcha_desc']),
576
				array('int', 'posts_require_captcha', 'subtext' => $txt['posts_require_captcha_desc'], 'onchange' => 'if (this.value > 0){ document.getElementById(\'guests_require_captcha\').checked = true; document.getElementById(\'guests_require_captcha\').disabled = true;} else {document.getElementById(\'guests_require_captcha\').disabled = false;}'),
577
			'',
578
			// PM Settings
579
				'pm1' => array('int', 'max_pm_recipients', 'subtext' => $txt['max_pm_recipients_note']),
580
				'pm2' => array('int', 'pm_posts_verification', 'subtext' => $txt['pm_posts_verification_note']),
581
				'pm3' => array('int', 'pm_posts_per_hour', 'subtext' => $txt['pm_posts_per_hour_note']),
582
			// Visual verification.
583
			array('title', 'configure_verification_means'),
584
			array('desc', 'configure_verification_means_desc'),
585
				'vv' => array('select', 'visual_verification_type', array($txt['setting_image_verification_off'], $txt['setting_image_verification_vsimple'], $txt['setting_image_verification_simple'], $txt['setting_image_verification_medium'], $txt['setting_image_verification_high'], $txt['setting_image_verification_extreme']), 'subtext'=> $txt['setting_visual_verification_type_desc'], 'onchange' => $context['use_graphic_library'] ? 'refreshImages();' : ''),
586
			// Clever Thomas, who is looking sheepy now? Not I, the mighty sword swinger did say.
587
			array('title', 'setup_verification_questions'),
588
			array('desc', 'setup_verification_questions_desc'),
589
				array('int', 'qa_verification_number', 'subtext' => $txt['setting_qa_verification_number_desc']),
590
				array('callback', 'question_answer_list'),
591
	);
592
593
	call_integration_hook('integrate_spam_settings', array(&$config_vars));
594
595
	if ($return_config)
596
		return $config_vars;
597
598
	// You need to be an admin to edit settings!
599
	isAllowedTo('admin_forum');
600
601
	// Firstly, figure out what languages we're dealing with, and do a little processing for the form's benefit.
602
	getLanguages();
603
	$context['qa_languages'] = array();
604
	foreach ($context['languages'] as $lang_id => $lang)
605
	{
606
		$lang_id = strtr($lang_id, array('-utf8' => ''));
607
		$lang['name'] = strtr($lang['name'], array('-utf8' => ''));
608
		$context['qa_languages'][$lang_id] = $lang;
609
	}
610
611
	// Secondly, load any questions we currently have.
612
	$context['question_answers'] = array();
613
	$request = $smcFunc['db_query']('', '
614
		SELECT id_question, lngfile, question, answers
615
		FROM {db_prefix}qanda'
616
	);
617
	while ($row = $smcFunc['db_fetch_assoc']($request))
618
	{
619
		$lang = strtr($row['lngfile'], array('-utf8' => ''));
620
		$context['question_answers'][$row['id_question']] = array(
621
			'lngfile' => $lang,
622
			'question' => $row['question'],
623
			'answers' => smf_json_decode($row['answers'], true),
624
		);
625
		$context['qa_by_lang'][$lang][] = $row['id_question'];
626
	}
627
628
	if (empty($context['qa_by_lang'][strtr($language, array('-utf8' => ''))]) && !empty($context['question_answers']))
629
	{
630
		if (empty($context['settings_insert_above']))
631
			$context['settings_insert_above'] = '';
632
633
		$context['settings_insert_above'] .= '<div class="noticebox">' . sprintf($txt['question_not_defined'], $context['languages'][$language]['name']) . '</div>';
634
	}
635
636
	// Thirdly, push some JavaScript for the form to make it work.
637
	addInlineJavaScript('
638
	var nextrow = ' . (!empty($context['question_answers']) ? max(array_keys($context['question_answers'])) + 1 : 1) . ';
639
	$(".qa_link a").click(function() {
640
		var id = $(this).parent().attr("id").substring(6);
641
		$("#qa_fs_" + id).show();
642
		$(this).parent().hide();
643
	});
644
	$(".qa_fieldset legend a").click(function() {
645
		var id = $(this).closest("fieldset").attr("id").substring(6);
646
		$("#qa_dt_" + id).show();
647
		$(this).closest("fieldset").hide();
648
	});
649
	$(".qa_add_question a").click(function() {
650
		var id = $(this).closest("fieldset").attr("id").substring(6);
651
		$(\'<dt><input type="text" name="question[\' + id + \'][\' + nextrow + \']" value="" size="50" class="input_text verification_question"></dt><dd><input type="text" name="answer[\' + id + \'][\' + nextrow + \'][]" value="" size="50" class="input_text verification_answer" / ><div class="qa_add_answer"><a href="javascript:void(0);" onclick="return addAnswer(this);">[ \' + ' . JavaScriptEscape($txt['setup_verification_add_answer']) . ' + \' ]</a></div></dd>\').insertBefore($(this).parent());
652
		nextrow++;
653
	});
654
	function addAnswer(obj)
655
	{
656
		var attr = $(obj).closest("dd").find(".verification_answer:last").attr("name");
657
		$(\'<input type="text" name="\' + attr + \'" value="" size="50" class="input_text verification_answer">\').insertBefore($(obj).closest("div"));
658
		return false;
659
	}
660
	$("#qa_dt_' . strtr($language, array('-utf8' => '')) . ' a").click();', true);
661
662
	// Will need the utility functions from here.
663
	require_once($sourcedir . '/ManageServer.php');
664
665
	// Saving?
666
	if (isset($_GET['save']))
667
	{
668
		checkSession();
669
670
		// Fix PM settings.
671
		$_POST['pm_spam_settings'] = (int) $_POST['max_pm_recipients'] . ',' . (int) $_POST['pm_posts_verification'] . ',' . (int) $_POST['pm_posts_per_hour'];
672
673
		// Hack in guest requiring verification!
674
		if (empty($_POST['posts_require_captcha']) && !empty($_POST['guests_require_captcha']))
675
			$_POST['posts_require_captcha'] = -1;
676
677
		$save_vars = $config_vars;
678
		unset($save_vars['pm1'], $save_vars['pm2'], $save_vars['pm3'], $save_vars['guest_verify']);
679
680
		$save_vars[] = array('text', 'pm_spam_settings');
681
682
		// Handle verification questions.
683
		$changes = array(
684
			'insert' => array(),
685
			'replace' => array(),
686
			'delete' => array(),
687
		);
688
		$qs_per_lang = array();
689
		foreach ($context['qa_languages'] as $lang_id => $dummy)
690
		{
691
			// If we had some questions for this language before, but don't now, delete everything from that language.
692
			if ((!isset($_POST['question'][$lang_id]) || !is_array($_POST['question'][$lang_id])) && !empty($context['qa_by_lang'][$lang_id]))
693
				$changes['delete'] = array_merge($questions['delete'], $context['qa_by_lang'][$lang_id]);
0 ignored issues
show
Bug introduced by
The variable $questions does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
694
695
			// Now step through and see if any existing questions no longer exist.
696
			if (!empty($context['qa_by_lang'][$lang_id]))
697
				foreach ($context['qa_by_lang'][$lang_id] as $q_id)
698
					if (empty($_POST['question'][$lang_id][$q_id]))
699
						$changes['delete'][] = $q_id;
700
701
			// Now let's see if there are new questions or ones that need updating.
702
			if (isset($_POST['question'][$lang_id]))
703
			{
704
				foreach ($_POST['question'][$lang_id] as $q_id => $question)
705
				{
706
					// Ignore junky ids.
707
					$q_id = (int) $q_id;
708
					if ($q_id <= 0)
709
						continue;
710
711
					// Check the question isn't empty (because they want to delete it?)
712 View Code Duplication
					if (empty($question) || trim($question) == '')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
713
					{
714
						if (isset($context['question_answers'][$q_id]))
715
							$changes['delete'][] = $q_id;
716
						continue;
717
					}
718
					$question = $smcFunc['htmlspecialchars'](trim($question));
719
720
					// Get the answers. Firstly check there actually might be some.
721
					if (!isset($_POST['answer'][$lang_id][$q_id]) || !is_array($_POST['answer'][$lang_id][$q_id]))
722
					{
723
						if (isset($context['question_answers'][$q_id]))
724
							$changes['delete'][] = $q_id;
725
						continue;
726
					}
727
					// Now get them and check that they might be viable.
728
					$answers = array();
729
					foreach ($_POST['answer'][$lang_id][$q_id] as $answer)
730
						if (!empty($answer) && trim($answer) !== '')
731
							$answers[] = $smcFunc['htmlspecialchars'](trim($answer));
732 View Code Duplication
					if (empty($answers))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
733
					{
734
						if (isset($context['question_answers'][$q_id]))
735
							$changes['delete'][] = $q_id;
736
						continue;
737
					}
738
					$answers = json_encode($answers);
739
740
					// At this point we know we have a question and some answers. What are we doing with it?
741
					if (!isset($context['question_answers'][$q_id]))
742
					{
743
						// New question. Now, we don't want to randomly consume ids, so we'll set those, rather than trusting the browser's supplied ids.
744
						$changes['insert'][] = array($lang_id, $question, $answers);
745
					}
746
					else
747
					{
748
						// It's an existing question. Let's see what's changed, if anything.
749
						if ($lang_id != $context['question_answers'][$q_id]['lngfile'] || $question != $context['question_answers'][$q_id]['question'] || $answers != $context['question_answers'][$q_id]['answers'])
750
							$changes['replace'][$q_id] = array('lngfile' => $lang_id, 'question' => $question, 'answers' => $answers);
751
					}
752
753
					if (!isset($qs_per_lang[$lang_id]))
754
						$qs_per_lang[$lang_id] = 0;
755
					$qs_per_lang[$lang_id]++;
756
				}
757
			}
758
		}
759
760
		// OK, so changes?
761
		if (!empty($changes['delete']))
762
		{
763
			$smcFunc['db_query']('', '
764
				DELETE FROM {db_prefix}qanda
765
				WHERE id_question IN ({array_int:questions})',
766
				array(
767
					'questions' => $changes['delete'],
768
				)
769
			);
770
		}
771
772
		if (!empty($changes['replace']))
773
		{
774
			foreach ($changes['replace'] as $q_id => $question)
775
			{
776
				$smcFunc['db_query']('', '
777
					UPDATE {db_prefix}qanda
778
					SET lngfile = {string:lngfile},
779
						question = {string:question},
780
						answers = {string:answers}
781
					WHERE id_question = {int:id_question}',
782
					array(
783
						'id_question' => $q_id,
784
						'lngfile' => $question['lngfile'],
785
						'question' => $question['question'],
786
						'answers' => $question['answers'],
787
					)
788
				);
789
			}
790
		}
791
792
		if (!empty($changes['insert']))
793
		{
794
			$smcFunc['db_insert']('insert',
795
				'{db_prefix}qanda',
796
				array('lngfile' => 'string-50', 'question' => 'string-255', 'answers' => 'string-65534'),
797
				$changes['insert'],
798
				array('id_question')
799
			);
800
		}
801
802
		// Lastly, the count of messages needs to be no more than the lowest number of questions for any one language.
803
		$count_questions = empty($qs_per_lang) ? 0 : min($qs_per_lang);
804
		if (empty($count_questions) || $_POST['qa_verification_number'] > $count_questions)
805
			$_POST['qa_verification_number'] = $count_questions;
806
807
		call_integration_hook('integrate_save_spam_settings', array(&$save_vars));
808
809
		// Now save.
810
		saveDBSettings($save_vars);
811
		$_SESSION['adm-save'] = true;
812
813
		cache_put_data('verificationQuestions', null, 300);
814
815
		redirectexit('action=admin;area=antispam');
816
	}
817
818
	$character_range = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P', 'R'), range('T', 'Y'));
819
	$_SESSION['visual_verification_code'] = '';
820
	for ($i = 0; $i < 6; $i++)
821
		$_SESSION['visual_verification_code'] .= $character_range[array_rand($character_range)];
822
823
	// Some javascript for CAPTCHA.
824
	$context['settings_post_javascript'] = '';
825
	if ($context['use_graphic_library'])
826
		$context['settings_post_javascript'] .= '
827
		function refreshImages()
828
		{
829
			var imageType = document.getElementById(\'visual_verification_type\').value;
830
			document.getElementById(\'verification_image\').src = \'' . $context['verification_image_href'] . ';type=\' + imageType;
831
		}';
832
833
	// Show the image itself, or text saying we can't.
834
	if ($context['use_graphic_library'])
835
		$config_vars['vv']['postinput'] = '<br><img src="' . $context['verification_image_href'] . ';type=' . (empty($modSettings['visual_verification_type']) ? 0 : $modSettings['visual_verification_type']) . '" alt="' . $txt['setting_image_verification_sample'] . '" id="verification_image"><br>';
836
	else
837
		$config_vars['vv']['postinput'] = '<br><span class="smalltext">' . $txt['setting_image_verification_nogd'] . '</span>';
838
839
	// Hack for PM spam settings.
840
	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
841
842
	// Hack for guests requiring verification.
843
	$modSettings['guests_require_captcha'] = !empty($modSettings['posts_require_captcha']);
844
	$modSettings['posts_require_captcha'] = !isset($modSettings['posts_require_captcha']) || $modSettings['posts_require_captcha'] == -1 ? 0 : $modSettings['posts_require_captcha'];
845
846
	// Some minor javascript for the guest post setting.
847
	if ($modSettings['posts_require_captcha'])
848
		$context['settings_post_javascript'] .= '
849
		document.getElementById(\'guests_require_captcha\').disabled = true;';
850
851
	// And everything else.
852
	$context['post_url'] = $scripturl . '?action=admin;area=antispam;save';
853
	$context['settings_title'] = $txt['antispam_Settings'];
854
	$context['page_title'] = $txt['antispam_title'];
855
	$context['sub_template'] = 'show_settings';
856
857
	$context[$context['admin_menu_name']]['tab_data'] = array(
858
		'title' => $txt['antispam_title'],
859
		'description' => $txt['antispam_Settings_desc'],
860
	);
861
862
	prepareDBSettingContext($config_vars);
863
}
864
865
/**
866
 * You'll never guess what this function does...
867
 *
868
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
869
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
870
 */
871
function ModifySignatureSettings($return_config = false)
872
{
873
	global $context, $txt, $modSettings, $sig_start, $smcFunc, $scripturl;
874
875
	$config_vars = array(
876
			// Are signatures even enabled?
877
			array('check', 'signature_enable'),
878
		'',
879
			// Tweaking settings!
880
			array('int', 'signature_max_length', 'subtext' => $txt['zero_for_no_limit']),
881
			array('int', 'signature_max_lines', 'subtext' => $txt['zero_for_no_limit']),
882
			array('int', 'signature_max_font_size', 'subtext' => $txt['zero_for_no_limit']),
883
			array('check', 'signature_allow_smileys', 'onclick' => 'document.getElementById(\'signature_max_smileys\').disabled = !this.checked;'),
884
			array('int', 'signature_max_smileys', 'subtext' => $txt['zero_for_no_limit']),
885
		'',
886
			// Image settings.
887
			array('int', 'signature_max_images', 'subtext' => $txt['signature_max_images_note']),
888
			array('int', 'signature_max_image_width', 'subtext' => $txt['zero_for_no_limit']),
889
			array('int', 'signature_max_image_height', 'subtext' => $txt['zero_for_no_limit']),
890
		'',
891
			array('bbc', 'signature_bbc'),
892
	);
893
894
	call_integration_hook('integrate_signature_settings', array(&$config_vars));
895
896
	if ($return_config)
897
		return $config_vars;
898
899
	// Setup the template.
900
	$context['page_title'] = $txt['signature_settings'];
901
	$context['sub_template'] = 'show_settings';
902
903
	// Disable the max smileys option if we don't allow smileys at all!
904
	$context['settings_post_javascript'] = 'document.getElementById(\'signature_max_smileys\').disabled = !document.getElementById(\'signature_allow_smileys\').checked;';
905
906
	// Load all the signature settings.
907
	list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
908
	$sig_limits = explode(',', $sig_limits);
909
	$disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
910
911
	// Applying to ALL signatures?!!
912
	if (isset($_GET['apply']))
913
	{
914
		// Security!
915
		checkSession('get');
916
917
		$sig_start = time();
918
		// This is horrid - but I suppose some people will want the option to do it.
919
		$_GET['step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0;
920
		$done = false;
921
922
		$request = $smcFunc['db_query']('', '
923
			SELECT MAX(id_member)
924
			FROM {db_prefix}members',
925
			array(
926
			)
927
		);
928
		list ($context['max_member']) = $smcFunc['db_fetch_row']($request);
929
		$smcFunc['db_free_result']($request);
930
931
		while (!$done)
932
		{
933
			$changes = array();
934
935
			$request = $smcFunc['db_query']('', '
936
				SELECT id_member, signature
937
				FROM {db_prefix}members
938
				WHERE id_member BETWEEN {int:step} AND {int:step} + 49
939
					AND id_group != {int:admin_group}
940
					AND FIND_IN_SET({int:admin_group}, additional_groups) = 0',
941
				array(
942
					'admin_group' => 1,
943
					'step' => $_GET['step'],
944
				)
945
			);
946
			while ($row = $smcFunc['db_fetch_assoc']($request))
947
			{
948
				// Apply all the rules we can realistically do.
949
				$sig = strtr($row['signature'], array('<br>' => "\n"));
950
951
				// Max characters...
952
				if (!empty($sig_limits[1]))
953
					$sig = $smcFunc['substr']($sig, 0, $sig_limits[1]);
954
				// Max lines...
955
				if (!empty($sig_limits[2]))
956
				{
957
					$count = 0;
958
					for ($i = 0; $i < strlen($sig); $i++)
959
					{
960
						if ($sig[$i] == "\n")
961
						{
962
							$count++;
963
							if ($count >= $sig_limits[2])
964
								$sig = substr($sig, 0, $i) . strtr(substr($sig, $i), array("\n" => ' '));
965
						}
966
					}
967
				}
968
969
				if (!empty($sig_limits[7]) && preg_match_all('~\[size=([\d\.]+)?(px|pt|em|x-large|larger)~i', $sig, $matches) !== false && isset($matches[2]))
970
				{
971
					foreach ($matches[1] as $ind => $size)
972
					{
973
						$limit_broke = 0;
974
						// Attempt to allow all sizes of abuse, so to speak.
975 View Code Duplication
						if ($matches[2][$ind] == 'px' && $size > $sig_limits[7])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
976
							$limit_broke = $sig_limits[7] . 'px';
977
						elseif ($matches[2][$ind] == 'pt' && $size > ($sig_limits[7] * 0.75))
978
							$limit_broke = ((int) $sig_limits[7] * 0.75) . 'pt';
979
						elseif ($matches[2][$ind] == 'em' && $size > ((float) $sig_limits[7] / 16))
980
							$limit_broke = ((float) $sig_limits[7] / 16) . 'em';
981
						elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18)
982
							$limit_broke = 'large';
983
984
						if ($limit_broke)
985
							$sig = str_replace($matches[0][$ind], '[size=' . $sig_limits[7] . 'px', $sig);
986
					}
987
				}
988
989
				// Stupid images - this is stupidly, stupidly challenging.
990
				if ((!empty($sig_limits[3]) || !empty($sig_limits[5]) || !empty($sig_limits[6])))
991
				{
992
					$replaces = array();
993
					$img_count = 0;
994
					// Get all BBC tags...
995
					preg_match_all('~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?(\s+width=([\d]+))?\s*\](?:<br>)*([^<">]+?)(?:<br>)*\[/img\]~i', $sig, $matches);
996
					// ... and all HTML ones.
997
					preg_match_all('~&lt;img\s+src=(?:&quot;)?((?:http://|ftp://|https://|ftps://).+?)(?:&quot;)?(?:\s+alt=(?:&quot;)?(.*?)(?:&quot;)?)?(?:\s?/)?&gt;~i', $sig, $matches2, PREG_PATTERN_ORDER);
998
					// And stick the HTML in the BBC.
999 View Code Duplication
					if (!empty($matches2))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1000
					{
1001
						foreach ($matches2[0] as $ind => $dummy)
1002
						{
1003
							$matches[0][] = $matches2[0][$ind];
1004
							$matches[1][] = '';
1005
							$matches[2][] = '';
1006
							$matches[3][] = '';
1007
							$matches[4][] = '';
1008
							$matches[5][] = '';
1009
							$matches[6][] = '';
1010
							$matches[7][] = $matches2[1][$ind];
1011
						}
1012
					}
1013
					// Try to find all the images!
1014
					if (!empty($matches))
1015
					{
1016
						$image_count_holder = array();
1017
						foreach ($matches[0] as $key => $image)
1018
						{
1019
							$width = -1; $height = -1;
1020
							$img_count++;
1021
							// Too many images?
1022
							if (!empty($sig_limits[3]) && $img_count > $sig_limits[3])
1023
							{
1024
								// If we've already had this before we only want to remove the excess.
1025
								if (isset($image_count_holder[$image]))
1026
								{
1027
									$img_offset = -1;
1028
									$rep_img_count = 0;
1029
									while ($img_offset !== false)
1030
									{
1031
										$img_offset = strpos($sig, $image, $img_offset + 1);
1032
										$rep_img_count++;
1033
										if ($rep_img_count > $image_count_holder[$image])
1034
										{
1035
											// Only replace the excess.
1036
											$sig = substr($sig, 0, $img_offset) . str_replace($image, '', substr($sig, $img_offset));
1037
											// Stop looping.
1038
											$img_offset = false;
1039
										}
1040
									}
1041
								}
1042
								else
1043
									$replaces[$image] = '';
1044
1045
								continue;
1046
							}
1047
1048
							// Does it have predefined restraints? Width first.
1049 View Code Duplication
							if ($matches[6][$key])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1050
								$matches[2][$key] = $matches[6][$key];
1051 View Code Duplication
							if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1052
							{
1053
								$width = $sig_limits[5];
1054
								$matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]);
1055
							}
1056
							elseif ($matches[2][$key])
1057
								$width = $matches[2][$key];
1058
							// ... and height.
1059 View Code Duplication
							if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1060
							{
1061
								$height = $sig_limits[6];
1062
								if ($width != -1)
1063
									$width = $width * ($height / $matches[4][$key]);
1064
							}
1065
							elseif ($matches[4][$key])
1066
								$height = $matches[4][$key];
1067
1068
							// If the dimensions are still not fixed - we need to check the actual image.
1069 View Code Duplication
							if (($width == -1 && $sig_limits[5]) || ($height == -1 && $sig_limits[6]))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1070
							{
1071
								$sizes = url_image_size($matches[7][$key]);
1072
								if (is_array($sizes))
1073
								{
1074
									// Too wide?
1075
									if ($sizes[0] > $sig_limits[5] && $sig_limits[5])
1076
									{
1077
										$width = $sig_limits[5];
1078
										$sizes[1] = $sizes[1] * ($width / $sizes[0]);
1079
									}
1080
									// Too high?
1081
									if ($sizes[1] > $sig_limits[6] && $sig_limits[6])
1082
									{
1083
										$height = $sig_limits[6];
1084
										if ($width == -1)
1085
											$width = $sizes[0];
1086
										$width = $width * ($height / $sizes[1]);
1087
									}
1088
									elseif ($width != -1)
1089
										$height = $sizes[1];
1090
								}
1091
							}
1092
1093
							// Did we come up with some changes? If so remake the string.
1094 View Code Duplication
							if ($width != -1 || $height != -1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1095
							{
1096
								$replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]';
1097
							}
1098
1099
							// Record that we got one.
1100
							$image_count_holder[$image] = isset($image_count_holder[$image]) ? $image_count_holder[$image] + 1 : 1;
1101
						}
1102
						if (!empty($replaces))
1103
							$sig = str_replace(array_keys($replaces), array_values($replaces), $sig);
1104
					}
1105
				}
1106
				// Try to fix disabled tags.
1107
				if (!empty($disabledTags))
1108
				{
1109
					$sig = preg_replace('~\[(?:' . implode('|', $disabledTags) . ').+?\]~i', '', $sig);
1110
					$sig = preg_replace('~\[/(?:' . implode('|', $disabledTags) . ')\]~i', '', $sig);
1111
				}
1112
1113
				$sig = strtr($sig, array("\n" => '<br>'));
1114
				call_integration_hook('integrate_apply_signature_settings', array(&$sig, $sig_limits, $disabledTags));
1115
				if ($sig != $row['signature'])
1116
					$changes[$row['id_member']] = $sig;
1117
			}
1118
			if ($smcFunc['db_num_rows']($request) == 0)
1119
				$done = true;
1120
			$smcFunc['db_free_result']($request);
1121
1122
			// Do we need to delete what we have?
1123 View Code Duplication
			if (!empty($changes))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1124
			{
1125
				foreach ($changes as $id => $sig)
1126
					$smcFunc['db_query']('', '
1127
						UPDATE {db_prefix}members
1128
						SET signature = {string:signature}
1129
						WHERE id_member = {int:id_member}',
1130
						array(
1131
							'id_member' => $id,
1132
							'signature' => $sig,
1133
						)
1134
					);
1135
			}
1136
1137
			$_GET['step'] += 50;
1138
			if (!$done)
1139
				pauseSignatureApplySettings();
1140
		}
1141
		$settings_applied = true;
1142
	}
1143
1144
	$context['signature_settings'] = array(
1145
		'enable' => isset($sig_limits[0]) ? $sig_limits[0] : 0,
1146
		'max_length' => isset($sig_limits[1]) ? $sig_limits[1] : 0,
1147
		'max_lines' => isset($sig_limits[2]) ? $sig_limits[2] : 0,
1148
		'max_images' => isset($sig_limits[3]) ? $sig_limits[3] : 0,
1149
		'allow_smileys' => isset($sig_limits[4]) && $sig_limits[4] == -1 ? 0 : 1,
1150
		'max_smileys' => isset($sig_limits[4]) && $sig_limits[4] != -1 ? $sig_limits[4] : 0,
1151
		'max_image_width' => isset($sig_limits[5]) ? $sig_limits[5] : 0,
1152
		'max_image_height' => isset($sig_limits[6]) ? $sig_limits[6] : 0,
1153
		'max_font_size' => isset($sig_limits[7]) ? $sig_limits[7] : 0,
1154
	);
1155
1156
	// Temporarily make each setting a modSetting!
1157
	foreach ($context['signature_settings'] as $key => $value)
1158
		$modSettings['signature_' . $key] = $value;
1159
1160
	// Make sure we check the right tags!
1161
	$modSettings['bbc_disabled_signature_bbc'] = $disabledTags;
1162
1163
	// Saving?
1164
	if (isset($_GET['save']))
1165
	{
1166
		checkSession();
1167
1168
		// Clean up the tag stuff!
1169
		$bbcTags = array();
1170
		foreach (parse_bbc(false) as $tag)
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The expression parse_bbc(false) of type string is not traversable.
Loading history...
1171
			$bbcTags[] = $tag['tag'];
1172
1173 View Code Duplication
		if (!isset($_POST['signature_bbc_enabledTags']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1174
			$_POST['signature_bbc_enabledTags'] = array();
1175
		elseif (!is_array($_POST['signature_bbc_enabledTags']))
1176
			$_POST['signature_bbc_enabledTags'] = array($_POST['signature_bbc_enabledTags']);
1177
1178
		$sig_limits = array();
1179
		foreach ($context['signature_settings'] as $key => $value)
1180
		{
1181
			if ($key == 'allow_smileys')
1182
				continue;
1183
			elseif ($key == 'max_smileys' && empty($_POST['signature_allow_smileys']))
1184
				$sig_limits[] = -1;
1185
			else
1186
				$sig_limits[] = !empty($_POST['signature_' . $key]) ? max(1, (int) $_POST['signature_' . $key]) : 0;
1187
		}
1188
1189
		call_integration_hook('integrate_save_signature_settings', array(&$sig_limits, &$bbcTags));
1190
1191
		$_POST['signature_settings'] = implode(',', $sig_limits) . ':' . implode(',', array_diff($bbcTags, $_POST['signature_bbc_enabledTags']));
1192
1193
		// Even though we have practically no settings let's keep the convention going!
1194
		$save_vars = array();
1195
		$save_vars[] = array('text', 'signature_settings');
1196
1197
		saveDBSettings($save_vars);
1198
		$_SESSION['adm-save'] = true;
1199
		redirectexit('action=admin;area=featuresettings;sa=sig');
1200
	}
1201
1202
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=sig';
1203
	$context['settings_title'] = $txt['signature_settings'];
1204
1205
	$context['settings_message'] = !empty($settings_applied) ? '<div class="infobox">' . $txt['signature_settings_applied'] . '</div>' : '<p class="centertext">' . sprintf($txt['signature_settings_warning'], $context['session_id'], $context['session_var']) . '</p>';
1206
1207
	prepareDBSettingContext($config_vars);
1208
}
1209
1210
/**
1211
 * Just pause the signature applying thing.
1212
 */
1213 View Code Duplication
function pauseSignatureApplySettings()
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1214
{
1215
	global $context, $txt, $sig_start;
1216
1217
	// Try get more time...
1218
	@set_time_limit(600);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1219
	if (function_exists('apache_reset_timeout'))
1220
		@apache_reset_timeout();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1221
1222
	// Have we exhausted all the time we allowed?
1223
	if (time() - array_sum(explode(' ', $sig_start)) < 3)
1224
		return;
1225
1226
	$context['continue_get_data'] = '?action=admin;area=featuresettings;sa=sig;apply;step=' . $_GET['step'] . ';' . $context['session_var'] . '=' . $context['session_id'];
1227
	$context['page_title'] = $txt['not_done_title'];
1228
	$context['continue_post_data'] = '';
1229
	$context['continue_countdown'] = '2';
1230
	$context['sub_template'] = 'not_done';
1231
1232
	// Specific stuff to not break this template!
1233
	$context[$context['admin_menu_name']]['current_subsection'] = 'sig';
1234
1235
	// Get the right percent.
1236
	$context['continue_percent'] = round(($_GET['step'] / $context['max_member']) * 100);
1237
1238
	// Never more than 100%!
1239
	$context['continue_percent'] = min($context['continue_percent'], 100);
1240
1241
	obExit();
1242
}
1243
1244
/**
1245
 * Show all the custom profile fields available to the user.
1246
 */
1247
function ShowCustomProfiles()
1248
{
1249
	global $txt, $scripturl, $context;
1250
	global $sourcedir;
1251
1252
	$context['page_title'] = $txt['custom_profile_title'];
1253
	$context['sub_template'] = 'show_custom_profile';
1254
1255
	// What about standard fields they can tweak?
1256
	$standard_fields = array('website', 'personal_text', 'timezone', 'posts', 'warning_status');
1257
	// What fields can't you put on the registration page?
1258
	$context['fields_no_registration'] = array('posts', 'warning_status');
1259
1260
	// Are we saving any standard field changes?
1261
	if (isset($_POST['save']))
1262
	{
1263
		checkSession();
1264
		validateToken('admin-scp');
1265
1266
		// Do the active ones first.
1267
		$disable_fields = array_flip($standard_fields);
1268
		if (!empty($_POST['active']))
1269
		{
1270
			foreach ($_POST['active'] as $value)
1271
				if (isset($disable_fields[$value]))
1272
					unset($disable_fields[$value]);
1273
		}
1274
		// What we have left!
1275
		$changes['disabled_profile_fields'] = empty($disable_fields) ? '' : implode(',', array_keys($disable_fields));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$changes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $changes = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1276
1277
		// Things we want to show on registration?
1278
		$reg_fields = array();
1279
		if (!empty($_POST['reg']))
1280
		{
1281
			foreach ($_POST['reg'] as $value)
1282
				if (in_array($value, $standard_fields) && !isset($disable_fields[$value]))
1283
					$reg_fields[] = $value;
1284
		}
1285
		// What we have left!
1286
		$changes['registration_fields'] = empty($reg_fields) ? '' : implode(',', $reg_fields);
1287
1288
		$_SESSION['adm-save'] = true;
1289
		if (!empty($changes))
1290
			updateSettings($changes);
1291
	}
1292
1293
	createToken('admin-scp');
1294
1295
	// Need to know the max order for custom fields
1296
	$context['custFieldsMaxOrder'] = custFieldsMaxOrder();
1297
1298
	require_once($sourcedir . '/Subs-List.php');
1299
1300
	$listOptions = array(
1301
		'id' => 'standard_profile_fields',
1302
		'title' => $txt['standard_profile_title'],
1303
		'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1304
		'get_items' => array(
1305
			'function' => 'list_getProfileFields',
1306
			'params' => array(
1307
				true,
1308
			),
1309
		),
1310
		'columns' => array(
1311
			'field' => array(
1312
				'header' => array(
1313
					'value' => $txt['standard_profile_field'],
1314
				),
1315
				'data' => array(
1316
					'db' => 'label',
1317
					'style' => 'width: 60%;',
1318
				),
1319
			),
1320
			'active' => array(
1321
				'header' => array(
1322
					'value' => $txt['custom_edit_active'],
1323
					'class' => 'centercol',
1324
				),
1325
				'data' => array(
1326 View Code Duplication
					'function' => function ($rowData)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1327
					{
1328
						$isChecked = $rowData['disabled'] ? '' : ' checked';
1329
						$onClickHandler = $rowData['can_show_register'] ? sprintf(' onclick="document.getElementById(\'reg_%1$s\').disabled = !this.checked;"', $rowData['id']) : '';
1330
						return sprintf('<input type="checkbox" name="active[]" id="active_%1$s" value="%1$s" class="input_check"%2$s%3$s>', $rowData['id'], $isChecked, $onClickHandler);
1331
					},
1332
					'style' => 'width: 20%;',
1333
					'class' => 'centercol',
1334
				),
1335
			),
1336
			'show_on_registration' => array(
1337
				'header' => array(
1338
					'value' => $txt['custom_edit_registration'],
1339
					'class' => 'centercol',
1340
				),
1341
				'data' => array(
1342 View Code Duplication
					'function' => function ($rowData)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1343
					{
1344
						$isChecked = $rowData['on_register'] && !$rowData['disabled'] ? ' checked' : '';
1345
						$isDisabled = $rowData['can_show_register'] ? '' : ' disabled';
1346
						return sprintf('<input type="checkbox" name="reg[]" id="reg_%1$s" value="%1$s" class="input_check"%2$s%3$s>', $rowData['id'], $isChecked, $isDisabled);
1347
					},
1348
					'style' => 'width: 20%;',
1349
					'class' => 'centercol',
1350
				),
1351
			),
1352
		),
1353
		'form' => array(
1354
			'href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1355
			'name' => 'standardProfileFields',
1356
			'token' => 'admin-scp',
1357
		),
1358
		'additional_rows' => array(
1359
			array(
1360
				'position' => 'below_table_data',
1361
				'value' => '<input type="submit" name="save" value="' . $txt['save'] . '" class="button_submit">',
1362
			),
1363
		),
1364
	);
1365
	createList($listOptions);
1366
1367
	$listOptions = array(
1368
		'id' => 'custom_profile_fields',
1369
		'title' => $txt['custom_profile_title'],
1370
		'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1371
		'default_sort_col' => 'field_order',
1372
		'no_items_label' => $txt['custom_profile_none'],
1373
		'items_per_page' => 25,
1374
		'get_items' => array(
1375
			'function' => 'list_getProfileFields',
1376
			'params' => array(
1377
				false,
1378
			),
1379
		),
1380
		'get_count' => array(
1381
			'function' => 'list_getProfileFieldSize',
1382
		),
1383
		'columns' => array(
1384
			'field_order' => array(
1385
				'header' => array(
1386
					'value' => $txt['custom_profile_fieldorder'],
1387
				),
1388
				'data' => array(
1389
					'function' => function ($rowData) use ($context, $txt, $scripturl)
1390
					{
1391
						$return = '<p class="centertext bold_text">'. $rowData['field_order'] .'<br />';
1392
1393 View Code Duplication
						if ($rowData['field_order'] > 1)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1394
							$return .= '<a href="' . $scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $rowData['id_field'] . ';move=up"><span class="toggle_up" title="'. $txt['custom_edit_order_move'] .' '. $txt['custom_edit_order_up'] .'"></span></a>';
1395
1396 View Code Duplication
						if ($rowData['field_order'] < $context['custFieldsMaxOrder'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1397
							$return .= '<a href="' . $scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $rowData['id_field'] . ';move=down"><span class="toggle_down" title="'. $txt['custom_edit_order_move'] .' '. $txt['custom_edit_order_down'] .'"></span></a>';
1398
1399
						$return .= '</p>';
1400
1401
						return $return;
1402
					},
1403
					'style' => 'width: 12%;',
1404
				),
1405
				'sort' => array(
1406
					'default' => 'field_order',
1407
					'reverse' => 'field_order DESC',
1408
				),
1409
			),
1410
			'field_name' => array(
1411
				'header' => array(
1412
					'value' => $txt['custom_profile_fieldname'],
1413
				),
1414
				'data' => array(
1415
					'function' => function ($rowData) use ($scripturl)
1416
					{
1417
						return sprintf('<a href="%1$s?action=admin;area=featuresettings;sa=profileedit;fid=%2$d">%3$s</a><div class="smalltext">%4$s</div>', $scripturl, $rowData['id_field'], $rowData['field_name'], $rowData['field_desc']);
1418
					},
1419
					'style' => 'width: 62%;',
1420
				),
1421
				'sort' => array(
1422
					'default' => 'field_name',
1423
					'reverse' => 'field_name DESC',
1424
				),
1425
			),
1426
			'field_type' => array(
1427
				'header' => array(
1428
					'value' => $txt['custom_profile_fieldtype'],
1429
				),
1430
				'data' => array(
1431
					'function' => function ($rowData) use ($txt)
1432
					{
1433
						$textKey = sprintf('custom_profile_type_%1$s', $rowData['field_type']);
1434
						return isset($txt[$textKey]) ? $txt[$textKey] : $textKey;
1435
					},
1436
					'style' => 'width: 15%;',
1437
					'class' => 'hidden',
1438
				),
1439
				'sort' => array(
1440
					'default' => 'field_type',
1441
					'reverse' => 'field_type DESC',
1442
				),
1443
			),
1444
			'active' => array(
1445
				'header' => array(
1446
					'value' => $txt['custom_profile_active'],
1447
				),
1448
				'data' => array(
1449
					'function' => function ($rowData) use ($txt)
1450
					{
1451
						return $rowData['active'] ? $txt['yes'] : $txt['no'];
1452
					},
1453
					'style' => 'width: 8%;',
1454
					'class' => 'hidden',
1455
				),
1456
				'sort' => array(
1457
					'default' => 'active DESC',
1458
					'reverse' => 'active',
1459
				),
1460
			),
1461
			'placement' => array(
1462
				'header' => array(
1463
					'value' => $txt['custom_profile_placement'],
1464
				),
1465
				'data' => array(
1466
					'function' => function ($rowData)
1467
					{
1468
						global $txt, $context;
1469
1470
						return $txt['custom_profile_placement_' . (empty($rowData['placement']) ? 'standard' : $context['cust_profile_fields_placement'][$rowData['placement']])];
1471
					},
1472
					'style' => 'width: 8%;',
1473
					'class' => 'hidden',
1474
				),
1475
				'sort' => array(
1476
					'default' => 'placement DESC',
1477
					'reverse' => 'placement',
1478
				),
1479
			),
1480
			'show_on_registration' => array(
1481
				'data' => array(
1482
					'sprintf' => array(
1483
						'format' => '<a href="' . $scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=%1$s">' . $txt['modify'] . '</a>',
1484
						'params' => array(
1485
							'id_field' => false,
1486
						),
1487
					),
1488
					'style' => 'width: 15%;',
1489
				),
1490
			),
1491
		),
1492
		'form' => array(
1493
			'href' => $scripturl . '?action=admin;area=featuresettings;sa=profileedit',
1494
			'name' => 'customProfileFields',
1495
		),
1496
		'additional_rows' => array(
1497
			array(
1498
				'position' => 'below_table_data',
1499
				'value' => '<input type="submit" name="new" value="' . $txt['custom_profile_make_new'] . '" class="button_submit">',
1500
			),
1501
		),
1502
	);
1503
	createList($listOptions);
1504
1505
	// There are two different ways we could get to this point. To keep it simple, they both do
1506
	// the same basic thing.
1507
	if (isset($_SESSION['adm-save']))
1508
	{
1509
		$context['saved_successful'] = true;
1510
		unset ($_SESSION['adm-save']);
1511
	}
1512
}
1513
1514
/**
1515
 * Callback for createList().
1516
 * @param int $start The item to start with (used for pagination purposes)
1517
 * @param int $items_per_page The number of items to display per page
1518
 * @param string $sort A string indicating how to sort the results
1519
 * @param bool $standardFields Whether or not to include standard fields as well
1520
 * @return array An array of info about the various profile fields
1521
 */
1522
function list_getProfileFields($start, $items_per_page, $sort, $standardFields)
1523
{
1524
	global $txt, $modSettings, $smcFunc;
1525
1526
	$list = array();
1527
1528
	if ($standardFields)
1529
	{
1530
		$standard_fields = array('website', 'personal_text', 'timezone', 'posts', 'warning_status');
1531
		$fields_no_registration = array('posts', 'warning_status');
1532
		$disabled_fields = isset($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
1533
		$registration_fields = isset($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array();
1534
1535
		foreach ($standard_fields as $field)
1536
			$list[] = array(
1537
				'id' => $field,
1538
				'label' => isset($txt['standard_profile_field_' . $field]) ? $txt['standard_profile_field_' . $field] : (isset($txt[$field]) ? $txt[$field] : $field),
1539
				'disabled' => in_array($field, $disabled_fields),
1540
				'on_register' => in_array($field, $registration_fields) && !in_array($field, $fields_no_registration),
1541
				'can_show_register' => !in_array($field, $fields_no_registration),
1542
			);
1543
	}
1544
	else
1545
	{
1546
		// Load all the fields.
1547
		$request = $smcFunc['db_query']('', '
1548
			SELECT id_field, col_name, field_name, field_desc, field_type, field_order, active, placement
1549
			FROM {db_prefix}custom_fields
1550
			ORDER BY {raw:sort}
1551
			LIMIT {int:start}, {int:items_per_page}',
1552
			array(
1553
				'sort' => $sort,
1554
				'start' => $start,
1555
				'items_per_page' => $items_per_page,
1556
			)
1557
		);
1558
		while ($row = $smcFunc['db_fetch_assoc']($request))
1559
			$list[] = $row;
1560
		$smcFunc['db_free_result']($request);
1561
	}
1562
1563
	return $list;
1564
}
1565
1566
/**
1567
 * Callback for createList().
1568
 * @return int The total number of custom profile fields
1569
 */
1570
function list_getProfileFieldSize()
1571
{
1572
	global $smcFunc;
1573
1574
	$request = $smcFunc['db_query']('', '
1575
		SELECT COUNT(*)
1576
		FROM {db_prefix}custom_fields',
1577
		array(
1578
		)
1579
	);
1580
1581
	list ($numProfileFields) = $smcFunc['db_fetch_row']($request);
1582
	$smcFunc['db_free_result']($request);
1583
1584
	return $numProfileFields;
1585
}
1586
1587
/**
1588
 * Edit some profile fields?
1589
 */
1590
function EditCustomProfiles()
1591
{
1592
	global $txt, $scripturl, $context, $smcFunc;
1593
1594
	// Sort out the context!
1595
	$context['fid'] = isset($_GET['fid']) ? (int) $_GET['fid'] : 0;
1596
	$context[$context['admin_menu_name']]['current_subsection'] = 'profile';
1597
	$context['page_title'] = $context['fid'] ? $txt['custom_edit_title'] : $txt['custom_add_title'];
1598
	$context['sub_template'] = 'edit_profile_field';
1599
1600
	// Load the profile language for section names.
1601
	loadLanguage('Profile');
1602
1603
	// There's really only a few places we can go...
1604
	$move_to = array('up', 'down');
1605
1606
	// We need this for both moving and saving so put it right here.
1607
	$order_count = custFieldsMaxOrder();
1608
1609
	if ($context['fid'])
1610
	{
1611
		$request = $smcFunc['db_query']('', '
1612
			SELECT
1613
				id_field, col_name, field_name, field_desc, field_type, field_order, field_length, field_options,
1614
				show_reg, show_display, show_mlist, show_profile, private, active, default_value, can_search,
1615
				bbc, mask, enclose, placement
1616
			FROM {db_prefix}custom_fields
1617
			WHERE id_field = {int:current_field}',
1618
			array(
1619
				'current_field' => $context['fid'],
1620
			)
1621
		);
1622
		$context['field'] = array();
1623
		while ($row = $smcFunc['db_fetch_assoc']($request))
1624
		{
1625
			if ($row['field_type'] == 'textarea')
1626
				@list ($rows, $cols) = @explode(',', $row['default_value']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1627
			else
1628
			{
1629
				$rows = 3;
1630
				$cols = 30;
1631
			}
1632
1633
			$context['field'] = array(
1634
				'name' => $row['field_name'],
1635
				'desc' => $row['field_desc'],
1636
				'col_name' => $row['col_name'],
1637
				'profile_area' => $row['show_profile'],
1638
				'reg' => $row['show_reg'],
1639
				'display' => $row['show_display'],
1640
				'mlist' => $row['show_mlist'],
1641
				'type' => $row['field_type'],
1642
				'order' => $row['field_order'],
1643
				'max_length' => $row['field_length'],
1644
				'rows' => $rows,
1645
				'cols' => $cols,
1646
				'bbc' => $row['bbc'] ? true : false,
1647
				'default_check' => $row['field_type'] == 'check' && $row['default_value'] ? true : false,
1648
				'default_select' => $row['field_type'] == 'select' || $row['field_type'] == 'radio' ? $row['default_value'] : '',
1649
				'options' => strlen($row['field_options']) > 1 ? explode(',', $row['field_options']) : array('', '', ''),
1650
				'active' => $row['active'],
1651
				'private' => $row['private'],
1652
				'can_search' => $row['can_search'],
1653
				'mask' => $row['mask'],
1654
				'regex' => substr($row['mask'], 0, 5) == 'regex' ? substr($row['mask'], 5) : '',
1655
				'enclose' => $row['enclose'],
1656
				'placement' => $row['placement'],
1657
			);
1658
		}
1659
		$smcFunc['db_free_result']($request);
1660
	}
1661
1662
	// Setup the default values as needed.
1663
	if (empty($context['field']))
1664
		$context['field'] = array(
1665
			'name' => '',
1666
			'col_name' => '???',
1667
			'desc' => '',
1668
			'profile_area' => 'forumprofile',
1669
			'reg' => false,
1670
			'display' => false,
1671
			'mlist' => false,
1672
			'type' => 'text',
1673
			'order' => 0,
1674
			'max_length' => 255,
1675
			'rows' => 4,
1676
			'cols' => 30,
1677
			'bbc' => false,
1678
			'default_check' => false,
1679
			'default_select' => '',
1680
			'options' => array('', '', ''),
1681
			'active' => true,
1682
			'private' => false,
1683
			'can_search' => false,
1684
			'mask' => 'nohtml',
1685
			'regex' => '',
1686
			'enclose' => '',
1687
			'placement' => 0,
1688
		);
1689
1690
	// Are we moving it?
1691
	if (isset($_GET['move']) && in_array($smcFunc['htmlspecialchars']($_GET['move']), $move_to))
1692
	{
1693
		// Down is the new up.
1694
		$new_order = ($_GET['move'] == 'up' ? ($context['field']['order'] - 1) : ($context['field']['order'] + 1));
1695
1696
		// Is this a valid position?
1697
		if ($new_order <= 0 || $new_order > $order_count)
1698
			redirectexit('action=admin;area=featuresettings;sa=profile'); // @todo implement an error handler
1699
1700
		// All good, proceed.
1701
		$smcFunc['db_query']('','
1702
			UPDATE {db_prefix}custom_fields
1703
			SET field_order = {int:old_order}
1704
			WHERE field_order = {int:new_order}',
1705
			array(
1706
				'new_order' => $new_order,
1707
				'old_order' => $context['field']['order'],
1708
			)
1709
		);
1710
		$smcFunc['db_query']('','
1711
			UPDATE {db_prefix}custom_fields
1712
			SET field_order = {int:new_order}
1713
			WHERE id_field = {int:id_field}',
1714
			array(
1715
				'new_order' => $new_order,
1716
				'id_field' => $context['fid'],
1717
			)
1718
		);
1719
		redirectexit('action=admin;area=featuresettings;sa=profile'); // @todo perhaps a nice confirmation message, dunno.
1720
	}
1721
1722
	// Are we saving?
1723
	if (isset($_POST['save']))
1724
	{
1725
		checkSession();
1726
		validateToken('admin-ecp');
1727
1728
		// Everyone needs a name - even the (bracket) unknown...
1729
		if (trim($_POST['field_name']) == '')
1730
			redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=need_name');
1731
1732
		// Regex you say?  Do a very basic test to see if the pattern is valid
1733
		if (!empty($_POST['regex']) && @preg_match($_POST['regex'], 'dummy') === false)
1734
			redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=regex_error');
1735
1736
		$_POST['field_name'] = $smcFunc['htmlspecialchars']($_POST['field_name']);
1737
		$_POST['field_desc'] = $smcFunc['htmlspecialchars']($_POST['field_desc']);
1738
1739
		// Checkboxes...
1740
		$show_reg = isset($_POST['reg']) ? (int) $_POST['reg'] : 0;
1741
		$show_display = isset($_POST['display']) ? 1 : 0;
1742
		$show_mlist = isset($_POST['mlist']) ? 1 : 0;
1743
		$bbc = isset($_POST['bbc']) ? 1 : 0;
1744
		$show_profile = $_POST['profile_area'];
1745
		$active = isset($_POST['active']) ? 1 : 0;
1746
		$private = isset($_POST['private']) ? (int) $_POST['private'] : 0;
1747
		$can_search = isset($_POST['can_search']) ? 1 : 0;
1748
1749
		// Some masking stuff...
1750
		$mask = isset($_POST['mask']) ? $_POST['mask'] : '';
1751
		if ($mask == 'regex' && isset($_POST['regex']))
1752
			$mask .= $_POST['regex'];
1753
1754
		$field_length = isset($_POST['max_length']) ? (int) $_POST['max_length'] : 255;
1755
		$enclose = isset($_POST['enclose']) ? $_POST['enclose'] : '';
1756
		$placement = isset($_POST['placement']) ? (int) $_POST['placement'] : 0;
1757
1758
		// Select options?
1759
		$field_options = '';
1760
		$newOptions = array();
1761
		$default = isset($_POST['default_check']) && $_POST['field_type'] == 'check' ? 1 : '';
1762
		if (!empty($_POST['select_option']) && ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio'))
1763
		{
1764
			foreach ($_POST['select_option'] as $k => $v)
1765
			{
1766
				// Clean, clean, clean...
1767
				$v = $smcFunc['htmlspecialchars']($v);
1768
				$v = strtr($v, array(',' => ''));
1769
1770
				// Nada, zip, etc...
1771
				if (trim($v) == '')
1772
					continue;
1773
1774
				// Otherwise, save it boy.
1775
				$field_options .= $v . ',';
1776
				// This is just for working out what happened with old options...
1777
				$newOptions[$k] = $v;
1778
1779
				// Is it default?
1780
				if (isset($_POST['default_select']) && $_POST['default_select'] == $k)
1781
					$default = $v;
1782
			}
1783
			$field_options = substr($field_options, 0, -1);
1784
		}
1785
1786
		// Text area has default has dimensions
1787
		if ($_POST['field_type'] == 'textarea')
1788
			$default = (int) $_POST['rows'] . ',' . (int) $_POST['cols'];
1789
1790
		// Come up with the unique name?
1791
		if (empty($context['fid']))
1792
		{
1793
			$col_name = $smcFunc['substr'](strtr($_POST['field_name'], array(' ' => '')), 0, 6);
1794
			preg_match('~([\w\d_-]+)~', $col_name, $matches);
1795
1796
			// If there is nothing to the name, then let's start out own - for foreign languages etc.
1797
			if (isset($matches[1]))
1798
				$col_name = $initial_col_name = 'cust_' . strtolower($matches[1]);
1799
			else
1800
				$col_name = $initial_col_name = 'cust_' . mt_rand(1, 9999);
1801
1802
			// Make sure this is unique.
1803
			$current_fields = array();
1804
			$request = $smcFunc['db_query']('', '
1805
				SELECT id_field, col_name
1806
				FROM {db_prefix}custom_fields');
1807
			while ($row = $smcFunc['db_fetch_assoc']($request))
1808
				$current_fields[$row['id_field']] = $row['col_name'];
1809
			$smcFunc['db_free_result']($request);
1810
1811
			$unique = false;
1812
			for ($i = 0; !$unique && $i < 9; $i ++)
1813
			{
1814
				if (!in_array($col_name, $current_fields))
1815
					$unique = true;
1816
				else
1817
					$col_name = $initial_col_name . $i;
1818
			}
1819
1820
			// Still not a unique column name? Leave it up to the user, then.
1821
			if (!$unique)
1822
				fatal_lang_error('custom_option_not_unique');
1823
		}
1824
		// Work out what to do with the user data otherwise...
1825
		else
1826
		{
1827
			// Anything going to check or select is pointless keeping - as is anything coming from check!
1828
			if (($_POST['field_type'] == 'check' && $context['field']['type'] != 'check')
1829
				|| (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && $context['field']['type'] != 'select' && $context['field']['type'] != 'radio')
1830
				|| ($context['field']['type'] == 'check' && $_POST['field_type'] != 'check'))
1831
			{
1832
				$smcFunc['db_query']('', '
1833
					DELETE FROM {db_prefix}themes
1834
					WHERE variable = {string:current_column}
1835
						AND id_member > {int:no_member}',
1836
					array(
1837
						'no_member' => 0,
1838
						'current_column' => $context['field']['col_name'],
1839
					)
1840
				);
1841
			}
1842
			// Otherwise - if the select is edited may need to adjust!
1843
			elseif ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio')
1844
			{
1845
				$optionChanges = array();
1846
				$takenKeys = array();
1847
				// Work out what's changed!
1848
				foreach ($context['field']['options'] as $k => $option)
1849
				{
1850
					if (trim($option) == '')
1851
						continue;
1852
1853
					// Still exists?
1854
					if (in_array($option, $newOptions))
1855
					{
1856
						$takenKeys[] = $k;
1857
						continue;
1858
					}
1859
				}
1860
1861
				// Finally - have we renamed it - or is it really gone?
1862
				foreach ($optionChanges as $k => $option)
1863
				{
1864
					// Just been renamed?
1865
					if (!in_array($k, $takenKeys) && !empty($newOptions[$k]))
1866
						$smcFunc['db_query']('', '
1867
							UPDATE {db_prefix}themes
1868
							SET value = {string:new_value}
1869
							WHERE variable = {string:current_column}
1870
								AND value = {string:old_value}
1871
								AND id_member > {int:no_member}',
1872
							array(
1873
								'no_member' => 0,
1874
								'new_value' => $newOptions[$k],
1875
								'current_column' => $context['field']['col_name'],
1876
								'old_value' => $option,
1877
							)
1878
						);
1879
				}
1880
			}
1881
			// @todo Maybe we should adjust based on new text length limits?
1882
		}
1883
1884
		// Do the insertion/updates.
1885
		if ($context['fid'])
1886
		{
1887
			$smcFunc['db_query']('', '
1888
				UPDATE {db_prefix}custom_fields
1889
				SET
1890
					field_name = {string:field_name}, field_desc = {string:field_desc},
1891
					field_type = {string:field_type}, field_length = {int:field_length},
1892
					field_options = {string:field_options}, show_reg = {int:show_reg},
1893
					show_display = {int:show_display}, show_mlist = {int:show_mlist}, show_profile = {string:show_profile},
1894
					private = {int:private}, active = {int:active}, default_value = {string:default_value},
1895
					can_search = {int:can_search}, bbc = {int:bbc}, mask = {string:mask},
1896
					enclose = {string:enclose}, placement = {int:placement}
1897
				WHERE id_field = {int:current_field}',
1898
				array(
1899
					'field_length' => $field_length,
1900
					'show_reg' => $show_reg,
1901
					'show_display' => $show_display,
1902
					'show_mlist' => $show_mlist,
1903
					'private' => $private,
1904
					'active' => $active,
1905
					'can_search' => $can_search,
1906
					'bbc' => $bbc,
1907
					'current_field' => $context['fid'],
1908
					'field_name' => $_POST['field_name'],
1909
					'field_desc' => $_POST['field_desc'],
1910
					'field_type' => $_POST['field_type'],
1911
					'field_options' => $field_options,
1912
					'show_profile' => $show_profile,
1913
					'default_value' => $default,
1914
					'mask' => $mask,
1915
					'enclose' => $enclose,
1916
					'placement' => $placement,
1917
				)
1918
			);
1919
1920
			// Just clean up any old selects - these are a pain!
1921
			if (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && !empty($newOptions))
1922
				$smcFunc['db_query']('', '
1923
					DELETE FROM {db_prefix}themes
1924
					WHERE variable = {string:current_column}
1925
						AND value NOT IN ({array_string:new_option_values})
1926
						AND id_member > {int:no_member}',
1927
					array(
1928
						'no_member' => 0,
1929
						'new_option_values' => $newOptions,
1930
						'current_column' => $context['field']['col_name'],
1931
					)
1932
				);
1933
		}
1934
		else
1935
		{
1936
			// Gotta figure it out the order.
1937
			$new_order = $order_count > 1 ? ($order_count + 1) : 1;
1938
1939
			$smcFunc['db_insert']('',
1940
				'{db_prefix}custom_fields',
1941
				array(
1942
					'col_name' => 'string', 'field_name' => 'string', 'field_desc' => 'string',
1943
					'field_type' => 'string', 'field_length' => 'string', 'field_options' => 'string', 'field_order' => 'int',
1944
					'show_reg' => 'int', 'show_display' => 'int', 'show_mlist' => 'int', 'show_profile' => 'string',
1945
					'private' => 'int', 'active' => 'int', 'default_value' => 'string', 'can_search' => 'int',
1946
					'bbc' => 'int', 'mask' => 'string', 'enclose' => 'string', 'placement' => 'int',
1947
				),
1948
				array(
1949
					$col_name, $_POST['field_name'], $_POST['field_desc'],
0 ignored issues
show
Bug introduced by
The variable $col_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...
1950
					$_POST['field_type'], $field_length, $field_options, $new_order,
1951
					$show_reg, $show_display, $show_mlist, $show_profile,
1952
					$private, $active, $default, $can_search,
1953
					$bbc, $mask, $enclose, $placement,
1954
				),
1955
				array('id_field')
1956
			);
1957
		}
1958
	}
1959
	// Deleting?
1960
	elseif (isset($_POST['delete']) && $context['field']['col_name'])
1961
	{
1962
		checkSession();
1963
		validateToken('admin-ecp');
1964
1965
		// Delete the user data first.
1966
		$smcFunc['db_query']('', '
1967
			DELETE FROM {db_prefix}themes
1968
			WHERE variable = {string:current_column}
1969
				AND id_member > {int:no_member}',
1970
			array(
1971
				'no_member' => 0,
1972
				'current_column' => $context['field']['col_name'],
1973
			)
1974
		);
1975
		// Finally - the field itself is gone!
1976
		$smcFunc['db_query']('', '
1977
			DELETE FROM {db_prefix}custom_fields
1978
			WHERE id_field = {int:current_field}',
1979
			array(
1980
				'current_field' => $context['fid'],
1981
			)
1982
		);
1983
1984
		// Re-arrange the order.
1985
		$smcFunc['db_query']('','
1986
			UPDATE {db_prefix}custom_fields
1987
			SET field_order = field_order - 1
1988
			WHERE field_order > {int:current_order}',
1989
			array(
1990
				'current_order' => $context['field']['order'],
1991
			)
1992
		);
1993
	}
1994
1995
	// Rebuild display cache etc.
1996
	if (isset($_POST['delete']) || isset($_POST['save']))
1997
	{
1998
		checkSession();
1999
2000
		$request = $smcFunc['db_query']('', '
2001
			SELECT col_name, field_name, field_type, field_order, bbc, enclose, placement, show_mlist
2002
			FROM {db_prefix}custom_fields
2003
			WHERE show_display = {int:is_displayed}
2004
				AND active = {int:active}
2005
				AND private != {int:not_owner_only}
2006
				AND private != {int:not_admin_only}
2007
			ORDER BY field_order',
2008
			array(
2009
				'is_displayed' => 1,
2010
				'active' => 1,
2011
				'not_owner_only' => 2,
2012
				'not_admin_only' => 3,
2013
			)
2014
		);
2015
2016
		$fields = array();
2017
		while ($row = $smcFunc['db_fetch_assoc']($request))
2018
		{
2019
			$fields[] = array(
2020
				'col_name' => strtr($row['col_name'], array('|' => '', ';' => '')),
2021
				'title' => strtr($row['field_name'], array('|' => '', ';' => '')),
2022
				'type' => $row['field_type'],
2023
				'order' => $row['field_order'],
2024
				'bbc' => $row['bbc'] ? '1' : '0',
2025
				'placement' => !empty($row['placement']) ? $row['placement'] : '0',
2026
				'enclose' => !empty($row['enclose']) ? $row['enclose'] : '',
2027
				'mlist' => $row['show_mlist'],
2028
			);
2029
		}
2030
		$smcFunc['db_free_result']($request);
2031
2032
		updateSettings(array('displayFields' => json_encode($fields)));
2033
		$_SESSION['adm-save'] = true;
2034
		redirectexit('action=admin;area=featuresettings;sa=profile');
2035
	}
2036
2037
	createToken('admin-ecp');
2038
}
2039
2040
/**
2041
 * Returns the maximum field_order value for the custom fields
2042
 * @return int The maximum value of field_order from the custom_fields table
2043
 */
2044
function custFieldsMaxOrder()
2045
{
2046
	global $smcFunc;
2047
2048
	// Gotta know the order limit
2049
	$result = $smcFunc['db_query']('', '
2050
			SELECT MAX(field_order)
2051
			FROM {db_prefix}custom_fields',
2052
			array()
2053
		);
2054
2055
	list ($order_count) = $smcFunc['db_fetch_row']($result);
2056
	$smcFunc['db_free_result']($result);
2057
2058
	return (int) $order_count;
2059
}
2060
2061
/**
2062
 * Allow to edit the settings on the pruning screen.
2063
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
2064
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
2065
 */
2066
function ModifyLogSettings($return_config = false)
2067
{
2068
	global $txt, $scripturl, $sourcedir, $context, $modSettings;
2069
2070
	// Make sure we understand what's going on.
2071
	loadLanguage('ManageSettings');
2072
2073
	$context['page_title'] = $txt['log_settings'];
2074
2075
	$config_vars = array(
2076
			array('check', 'modlog_enabled', 'help' => 'modlog'),
2077
			array('check', 'adminlog_enabled', 'help' => 'adminlog'),
2078
			array('check', 'userlog_enabled', 'help' => 'userlog'),
2079
			// The error log is a wonderful thing.
2080
			array('title', 'errlog'),
2081
			array('desc', 'error_log_desc'),
2082
			array('check', 'enableErrorLogging'),
2083
			array('check', 'enableErrorQueryLogging'),
2084
			array('check', 'log_ban_hits'),
2085
			// Even do the pruning?
2086
			array('title', 'pruning_title'),
2087
			array('desc', 'pruning_desc'),
2088
			// The array indexes are there so we can remove/change them before saving.
2089
			'pruningOptions' => array('check', 'pruningOptions'),
2090
		'',
2091
			// Various logs that could be pruned.
2092
			array('int', 'pruneErrorLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Error log.
2093
			array('int', 'pruneModLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Moderation log.
2094
			array('int', 'pruneBanLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Ban hit log.
2095
			array('int', 'pruneReportLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Report to moderator log.
2096
			array('int', 'pruneScheduledTaskLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Log of the scheduled tasks and how long they ran.
2097
			array('int', 'pruneSpiderHitLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Log of the scheduled tasks and how long they ran.
2098
			// If you add any additional logs make sure to add them after this point.  Additionally, make sure you add them to the weekly scheduled task.
2099
			// Mod Developers: Do NOT use the pruningOptions master variable for this as SMF Core may overwrite your setting in the future!
2100
	);
2101
2102
	// We want to be toggling some of these for a nice user experience. If you want to add yours to the list of those magically hidden when the 'pruning' option is off, add to this.
2103
	$prune_toggle = array('pruneErrorLog', 'pruneModLog', 'pruneBanLog', 'pruneReportLog', 'pruneScheduledTaskLog', 'pruneSpiderHitLog');
2104
2105
	call_integration_hook('integrate_prune_settings', array(&$config_vars, &$prune_toggle, false));
2106
2107
	$prune_toggle_dt = array();
2108
	foreach ($prune_toggle as $item)
2109
		$prune_toggle_dt[] = 'setting_' . $item;
2110
2111
	if ($return_config)
2112
		return $config_vars;
2113
2114
	addInlineJavaScript('
2115
	function togglePruned()
2116
	{
2117
		var newval = $("#pruningOptions").prop("checked");
2118
		$("#' . implode(', #', $prune_toggle) . '").closest("dd").toggle(newval);
2119
		$("#' . implode(', #', $prune_toggle_dt) . '").closest("dt").toggle(newval);
2120
	};
2121
	togglePruned();
2122
	$("#pruningOptions").click(function() { togglePruned(); });', true);
2123
2124
	// We'll need this in a bit.
2125
	require_once($sourcedir . '/ManageServer.php');
2126
2127
	// Saving?
2128
	if (isset($_GET['save']))
2129
	{
2130
		checkSession();
2131
2132
		// Because of the excitement attached to combining pruning log items, we need to duplicate everything here.
2133
		$savevar = array(
2134
			array('check', 'modlog_enabled'),
2135
			array('check', 'adminlog_enabled'),
2136
			array('check', 'userlog_enabled'),
2137
			array('check', 'enableErrorLogging'),
2138
			array('check', 'enableErrorQueryLogging'),
2139
			array('check', 'log_ban_hits'),
2140
			array('text', 'pruningOptions')
2141
		);
2142
2143
		call_integration_hook('integrate_prune_settings', array(&$savevar, &$prune_toggle, true));
2144
2145
		if (!empty($_POST['pruningOptions']))
2146
		{
2147
			$vals = array();
2148
			foreach ($config_vars as $index => $dummy)
2149
			{
2150
				if (!is_array($dummy) || $index == 'pruningOptions' || !in_array($dummy[1], $prune_toggle))
2151
					continue;
2152
2153
				$vals[] = empty($_POST[$dummy[1]]) || $_POST[$dummy[1]] < 0 ? 0 : (int) $_POST[$dummy[1]];
2154
			}
2155
			$_POST['pruningOptions'] = implode(',', $vals);
2156
		}
2157
		else
2158
			$_POST['pruningOptions'] = '';
2159
2160
		saveDBSettings($savevar);
2161
		$_SESSION['adm-save'] = true;
2162
		redirectexit('action=admin;area=logs;sa=settings');
2163
	}
2164
2165
	$context['post_url'] = $scripturl . '?action=admin;area=logs;save;sa=settings';
2166
	$context['settings_title'] = $txt['log_settings'];
2167
	$context['sub_template'] = 'show_settings';
2168
2169
	// Get the actual values
2170
	if (!empty($modSettings['pruningOptions']))
2171
		@list ($modSettings['pruneErrorLog'], $modSettings['pruneModLog'], $modSettings['pruneBanLog'], $modSettings['pruneReportLog'], $modSettings['pruneScheduledTaskLog'], $modSettings['pruneSpiderHitLog']) = explode(',', $modSettings['pruningOptions']);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2172
	else
2173
		$modSettings['pruneErrorLog'] = $modSettings['pruneModLog'] = $modSettings['pruneBanLog'] = $modSettings['pruneReportLog'] = $modSettings['pruneScheduledTaskLog'] = $modSettings['pruneSpiderHitLog'] = 0;
2174
2175
	prepareDBSettingContext($config_vars);
2176
}
2177
2178
/**
2179
 * If you have a general mod setting to add stick it here.
2180
 *
2181
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
2182
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
2183
 */
2184
function ModifyGeneralModSettings($return_config = false)
2185
{
2186
	global $txt, $scripturl, $context;
2187
2188
	$config_vars = array(
2189
		// Mod authors, add any settings UNDER this line. Include a comma at the end of the line and don't remove this statement!!
2190
	);
2191
2192
	// Make it even easier to add new settings.
2193
	call_integration_hook('integrate_general_mod_settings', array(&$config_vars));
2194
2195
	if ($return_config)
2196
		return $config_vars;
2197
2198
	$context['post_url'] = $scripturl . '?action=admin;area=modsettings;save;sa=general';
2199
	$context['settings_title'] = $txt['mods_cat_modifications_misc'];
2200
2201
	// No removing this line you, dirty unwashed mod authors. :p
2202
	if (empty($config_vars))
2203
	{
2204
		$context['settings_save_dont_show'] = true;
2205
		$context['settings_message'] = '<div class="centertext">' . $txt['modification_no_misc_settings'] . '</div>';
2206
2207
		return prepareDBSettingContext($config_vars);
2208
	}
2209
2210
	// Saving?
2211 View Code Duplication
	if (isset($_GET['save']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2212
	{
2213
		checkSession();
2214
2215
		$save_vars = $config_vars;
2216
2217
		call_integration_hook('integrate_save_general_mod_settings', array(&$save_vars));
2218
2219
		// This line is to help mod authors do a search/add after if you want to add something here. Keyword: FOOT TAPPING SUCKS!
2220
		saveDBSettings($save_vars);
2221
2222
		// This line is to remind mod authors that it's nice to let the users know when something has been saved.
2223
		$_SESSION['adm-save'] = true;
2224
2225
		// This line is to help mod authors do a search/add after if you want to add something here. Keyword: I LOVE TEA!
2226
		redirectexit('action=admin;area=modsettings;sa=general');
2227
	}
2228
2229
	// This line is to help mod authors do a search/add after if you want to add something here. Keyword: RED INK IS FOR TEACHERS AND THOSE WHO LIKE PAIN!
2230
	prepareDBSettingContext($config_vars);
2231
}
2232
2233
/**
2234
 * Handles modifying the alerts settings
2235
 */
2236
function ModifyAlertsSettings()
2237
{
2238
	global $context, $modSettings, $sourcedir, $txt;
2239
2240
	// Dummy settings for the template...
2241
	$modSettings['allow_disableAnnounce'] = false;
2242
	$context['user']['is_owner'] = false;
2243
	$context['member'] = array();
2244
	$context['id_member'] = 0;
2245
	$context['menu_item_selected'] = 'alerts';
2246
	$context['token_check'] = 'noti-admin';
2247
2248
	// Specify our action since we'll want to post back here instead of the profile
2249
	$context['action'] = 'action=admin;area=featuresettings;sa=alerts;'. $context['session_var'] .'='. $context['session_id'];
2250
2251
	loadTemplate('Profile');
2252
	loadLanguage('Profile');
2253
2254
	include_once($sourcedir . '/Profile-Modify.php');
2255
	alert_configuration(0);
2256
2257
	$context['page_title'] = $txt['notify_settings'];
2258
2259
	// Override the description
2260
	$context['description'] = $txt['notifications_desc'];
2261
	$context['sub_template'] = 'alert_configuration';
2262
}
2263
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
2264