Failed Conditions
Branch release-2.1 (4e22cf)
by Rick
06:39
created

ManageSettings.php ➔ ModifyMentionsSettings()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 17
nc 3
nop 1
dl 0
loc 31
rs 8.8571
c 0
b 0
f 0
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 2017 Simple Machines and individual contributors
12
 * @license http://www.simplemachines.org/about/smf/license.php BSD
13
 *
14
 * @version 2.1 Beta 4
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.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $defaultAction not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
25
 */
26
function loadGeneralSettingParameters($subActions = array(), $defaultAction = null)
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
	// If no fallback was specified, use the first subaction.
42
	$defaultAction = $defaultAction ?: key($subActions);
43
44
	// I want...
45
	$_REQUEST['sa'] = isset($_REQUEST['sa'], $subActions[$_REQUEST['sa']]) ? $_REQUEST['sa'] : $defaultAction;
46
	$context['sub_action'] = $_REQUEST['sa'];
47
}
48
49
/**
50
 * This function passes control through to the relevant tab.
51
 */
52
function ModifyFeatureSettings()
53
{
54
	global $context, $txt, $settings;
55
56
	$context['page_title'] = $txt['modSettings_title'];
57
58
	$subActions = array(
59
		'basic' => 'ModifyBasicSettings',
60
		'bbc' => 'ModifyBBCSettings',
61
		'layout' => 'ModifyLayoutSettings',
62
		'sig' => 'ModifySignatureSettings',
63
		'profile' => 'ShowCustomProfiles',
64
		'profileedit' => 'EditCustomProfiles',
65
		'likes' => 'ModifyLikesSettings',
66
		'mentions' => 'ModifyMentionsSettings',
67
		'alerts' => 'ModifyAlertsSettings',
68
	);
69
70
	loadGeneralSettingParameters($subActions, 'basic');
71
72
	// Load up all the tabs...
73
	$context[$context['admin_menu_name']]['tab_data'] = array(
74
		'title' => $txt['modSettings_title'],
75
		'help' => 'featuresettings',
76
		'description' => sprintf($txt['modSettings_desc'], $settings['theme_id'], $context['session_id'], $context['session_var']),
77
		'tabs' => array(
78
			'basic' => array(
79
			),
80
			'bbc' => array(
81
				'description' => $txt['manageposts_bbc_settings_description'],
82
			),
83
			'layout' => array(
84
			),
85
			'sig' => array(
86
				'description' => $txt['signature_settings_desc'],
87
			),
88
			'profile' => array(
89
				'description' => $txt['custom_profile_desc'],
90
			),
91
			'likes' => array(
92
			),
93
			'mentions' => array(
94
			),
95
			'alerts' => array(
96
				'description' => $txt['notifications_desc'],
97
			),
98
		),
99
	);
100
101
	call_integration_hook('integrate_modify_features', array(&$subActions));
102
103
	// Call the right function for this sub-action.
104
	call_helper($subActions[$_REQUEST['sa']]);
105
}
106
107
/**
108
 * This my friend, is for all the mod authors out there.
109
 */
110
function ModifyModSettings()
111
{
112
	global $context, $txt;
113
114
	$context['page_title'] = $txt['admin_modifications'];
115
116
	$subActions = array(
117
		'general' => 'ModifyGeneralModSettings',
118
		// Mod authors, once again, if you have a whole section to add do it AFTER this line, and keep a comma at the end.
119
	);
120
121
	// Make it easier for mods to add new areas.
122
	call_integration_hook('integrate_modify_modifications', array(&$subActions));
123
124
	loadGeneralSettingParameters($subActions, 'general');
125
126
	// Load up all the tabs...
127
	$context[$context['admin_menu_name']]['tab_data'] = array(
128
		'title' => $txt['admin_modifications'],
129
		'help' => 'modsettings',
130
		'description' => $txt['modification_settings_desc'],
131
		'tabs' => array(
132
			'general' => array(
133
			),
134
		),
135
	);
136
137
	// Call the right function for this sub-action.
138
	call_helper($subActions[$_REQUEST['sa']]);
139
}
140
141
/**
142
 * Config array for changing the basic forum settings
143
 * Accessed  from ?action=admin;area=featuresettings;sa=basic;
144
 *
145
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
146
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<*,string|string[]|...ring|array>|array>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
147
 */
148
function ModifyBasicSettings($return_config = false)
149
{
150
	global $txt, $scripturl, $context, $modSettings;
151
152
	// We need to know if personal text is enabled, and if it's in the registration fields option.
153
	// If admins have set it up as an on-registration thing, they can't set a default value (because it'll never be used)
154
	$disabled_fields = isset($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
155
	$reg_fields = isset($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array();
156
	$can_personal_text = !in_array('personal_text', $disabled_fields) && !in_array('personal_text', $reg_fields);
157
158
	$config_vars = array(
159
			// Big Options... polls, sticky, bbc....
160
			array('select', 'pollMode', array($txt['disable_polls'], $txt['enable_polls'], $txt['polls_as_topics'])),
161
		'',
162
			// Basic stuff, titles, flash, permissions...
163
			array('check', 'allow_guestAccess'),
164
			array('check', 'enable_buddylist'),
165
			array('check', 'allow_hideOnline'),
166
			array('check', 'titlesEnable'),
167
			array('text', 'default_personal_text', 'subtext' => $txt['default_personal_text_note'], 'disabled' => !$can_personal_text),
168
			array('check', 'topic_move_any'),
169
			array('int', 'defaultMaxListItems', 'step' => 1, 'min' => 1, 'max' => 999),
170
		'',
171
			// Jquery source
172
			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;}'),
173
			array('text', 'jquery_custom', 'disabled' => isset($modSettings['jquery_source']) && $modSettings['jquery_source'] != 'custom', 'size' => 75),
174
		'',
175
			// css and js minification.
176
			array('check', 'minimize_files'),
177
		'',
178
			// SEO stuff
179
			array('check', 'queryless_urls', 'subtext' => '<strong>' . $txt['queryless_urls_note'] . '</strong>'),
180
			array('text', 'meta_keywords', 'subtext' => $txt['meta_keywords_note'], 'size' => 50),
181
		'',
182
			// Number formatting, timezones.
183
			array('text', 'time_format'),
184
			array('float', 'time_offset', 'subtext' => $txt['setting_time_offset_note'], 6, 'postinput' => $txt['hours'], 'step' => 0.25, 'min' => -23.5, 'max' => 23.5),
185
			'default_timezone' => array('select', 'default_timezone', array()),
186
			array('text', 'timezone_priority_countries', 'subtext' => $txt['setting_timezone_priority_countries_note']),
187
		'',
188
			// Who's online?
189
			array('check', 'who_enabled'),
190
			array('int', 'lastActive', 6, 'postinput' => $txt['minutes']),
191
		'',
192
			// Statistics.
193
			array('check', 'trackStats'),
194
			array('check', 'hitStats'),
195
		'',
196
			// Option-ish things... miscellaneous sorta.
197
			array('check', 'allow_disableAnnounce'),
198
			array('check', 'disallow_sendBody'),
199
		'',
200
			// Alerts stuff
201
			array('check', 'enable_ajax_alerts'),
202
	);
203
204
	// Get all the time zones.
205
	if (function_exists('timezone_identifiers_list') && function_exists('date_default_timezone_set'))
206
	{
207
		$all_zones = timezone_identifiers_list();
208
		// Make sure we set the value to the same as the printed value.
209
		foreach ($all_zones as $zone)
210
			$config_vars['default_timezone'][2][$zone] = $zone;
211
	}
212
	else
213
		unset($config_vars['default_timezone']);
214
215
	call_integration_hook('integrate_modify_basic_settings', array(&$config_vars));
216
217
	if ($return_config)
218
		return $config_vars;
219
220
	// Saving?
221
	if (isset($_GET['save']))
222
	{
223
		checkSession();
224
225
		// Prevent absurd boundaries here - make it a day tops.
226
		if (isset($_POST['lastActive']))
227
			$_POST['lastActive'] = min((int) $_POST['lastActive'], 1440);
228
229
		call_integration_hook('integrate_save_basic_settings');
230
231
		saveDBSettings($config_vars);
232
		$_SESSION['adm-save'] = true;
233
234
		writeLog();
235
		redirectexit('action=admin;area=featuresettings;sa=basic');
236
	}
237
238
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=basic';
239
	$context['settings_title'] = $txt['mods_cat_features'];
240
241
	prepareDBSettingContext($config_vars);
242
}
243
244
/**
245
 * Set a few Bulletin Board Code settings. It loads a list of Bulletin Board Code tags to allow disabling tags.
246
 * Requires the admin_forum permission.
247
 * Accessed from ?action=admin;area=featuresettings;sa=bbc.
248
 *
249
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
250
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string[]|array<*,s...g|integer>|string>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
251
 * @uses Admin template, edit_bbc_settings sub-template.
252
 */
253
function ModifyBBCSettings($return_config = false)
254
{
255
	global $context, $txt, $modSettings, $scripturl, $sourcedir;
256
257
	$config_vars = array(
258
			// Main tweaks
259
			array('check', 'enableBBC'),
260
			array('check', 'enableBBC', 0, 'onchange' => 'toggleBBCDisabled(\'disabledBBC\', !this.checked);'),
261
			array('check', 'enablePostHTML'),
262
			array('check', 'autoLinkUrls'),
263
		'',
264
			array('bbc', 'disabledBBC'),
265
	);
266
267
	$context['settings_post_javascript'] = '
268
		toggleBBCDisabled(\'disabledBBC\', ' . (empty($modSettings['enableBBC']) ? 'true' : 'false') . ');';
269
270
	call_integration_hook('integrate_modify_bbc_settings', array(&$config_vars));
271
272
	if ($return_config)
273
		return $config_vars;
274
275
	// Setup the template.
276
	require_once($sourcedir . '/ManageServer.php');
277
	$context['sub_template'] = 'show_settings';
278
	$context['page_title'] = $txt['manageposts_bbc_settings_title'];
279
280
	// Make sure we check the right tags!
281
	$modSettings['bbc_disabled_disabledBBC'] = empty($modSettings['disabledBBC']) ? array() : explode(',', $modSettings['disabledBBC']);
282
283
	// Saving?
284
	if (isset($_GET['save']))
285
	{
286
		checkSession();
287
288
		// Clean up the tags.
289
		$bbcTags = array();
290
		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...
291
			$bbcTags[] = $tag['tag'];
292
293 View Code Duplication
		if (!isset($_POST['disabledBBC_enabledTags']))
294
			$_POST['disabledBBC_enabledTags'] = array();
295
		elseif (!is_array($_POST['disabledBBC_enabledTags']))
296
			$_POST['disabledBBC_enabledTags'] = array($_POST['disabledBBC_enabledTags']);
297
		// Work out what is actually disabled!
298
		$_POST['disabledBBC'] = implode(',', array_diff($bbcTags, $_POST['disabledBBC_enabledTags']));
299
300
		call_integration_hook('integrate_save_bbc_settings', array($bbcTags));
301
302
		saveDBSettings($config_vars);
303
		$_SESSION['adm-save'] = true;
304
		redirectexit('action=admin;area=featuresettings;sa=bbc');
305
	}
306
307
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=bbc';
308
	$context['settings_title'] = $txt['manageposts_bbc_settings_title'];
309
310
	prepareDBSettingContext($config_vars);
311
}
312
313
/**
314
 * Allows modifying the global layout settings in the forum
315
 * Accessed through ?action=admin;area=featuresettings;sa=layout;
316
 *
317
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
318
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string[]|array<str...ray<string|array>>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
319
 */
320
function ModifyLayoutSettings($return_config = false)
321
{
322
	global $txt, $scripturl, $context;
323
324
	$config_vars = array(
325
			// Pagination stuff.
326
			array('check', 'compactTopicPagesEnable'),
327
			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>'),
328
			array('int', 'defaultMaxMembers'),
329
		'',
330
			// Stuff that just is everywhere - today, search, online, etc.
331
			array('select', 'todayMod', array($txt['today_disabled'], $txt['today_only'], $txt['yesterday_today'])),
332
			array('check', 'onlineEnable'),
333
		'',
334
			// This is like debugging sorta.
335
			array('check', 'timeLoadPageEnable'),
336
	);
337
338
	call_integration_hook('integrate_layout_settings', array(&$config_vars));
339
340
	if ($return_config)
341
		return $config_vars;
342
343
	// Saving?
344 View Code Duplication
	if (isset($_GET['save']))
345
	{
346
		checkSession();
347
348
		call_integration_hook('integrate_save_layout_settings');
349
350
		saveDBSettings($config_vars);
351
		$_SESSION['adm-save'] = true;
352
		writeLog();
353
354
		redirectexit('action=admin;area=featuresettings;sa=layout');
355
	}
356
357
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=layout';
358
	$context['settings_title'] = $txt['mods_cat_layout'];
359
360
	prepareDBSettingContext($config_vars);
361
}
362
363
/**
364
 * Config array for changing like settings
365
 * Accessed  from ?action=admin;area=featuresettings;sa=likes;
366
 *
367
 * @param bool $return_config Whether or not to return the config_vars array
368
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string[][]|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
369
 */
370
function ModifyLikesSettings($return_config = false)
371
{
372
	global $txt, $scripturl, $context;
373
374
	$config_vars = array(
375
		array('check', 'enable_likes'),
376
		array('permissions', 'likes_view'),
377
		array('permissions', 'likes_like'),
378
	);
379
380
	call_integration_hook('integrate_likes_settings', array(&$config_vars));
381
382
	if ($return_config)
383
		return $config_vars;
384
385
	// Saving?
386
	if (isset($_GET['save']))
387
	{
388
		checkSession();
389
390
		call_integration_hook('integrate_save_likes_settings');
391
392
		saveDBSettings($config_vars);
393
		$_SESSION['adm-save'] = true;
394
		redirectexit('action=admin;area=featuresettings;sa=likes');
395
	}
396
397
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=likes';
398
	$context['settings_title'] = $txt['likes'];
399
400
	prepareDBSettingContext($config_vars);
401
}
402
403
/**
404
 * Config array for changing like settings
405
 * Accessed  from ?action=admin;area=featuresettings;sa=mentions;
406
 *
407
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
408
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string[][]|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
409
 */
410
function ModifyMentionsSettings($return_config = false)
411
{
412
	global $txt, $scripturl, $context;
413
414
	$config_vars = array(
415
		array('check', 'enable_mentions'),
416
		array('permissions', 'mention'),
417
	);
418
419
	call_integration_hook('integrate_mentions_settings', array(&$config_vars));
420
421
	if ($return_config)
422
		return $config_vars;
423
424
	// Saving?
425
	if (isset($_GET['save']))
426
	{
427
		checkSession();
428
429
		call_integration_hook('integrate_save_mentions_settings');
430
431
		saveDBSettings($config_vars);
432
		$_SESSION['adm-save'] = true;
433
		redirectexit('action=admin;area=featuresettings;sa=mentions');
434
	}
435
436
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=mentions';
437
	$context['settings_title'] = $txt['mentions'];
438
439
	prepareDBSettingContext($config_vars);
440
}
441
442
/**
443
 * Moderation type settings - although there are fewer than we have you believe ;)
444
 *
445
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
446
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
447
 */
448
function ModifyWarningSettings($return_config = false)
449
{
450
	global $txt, $scripturl, $context, $modSettings, $sourcedir;
451
452
	// You need to be an admin to edit settings!
453
	isAllowedTo('admin_forum');
454
455
	loadLanguage('Help');
456
	loadLanguage('ManageSettings');
457
458
	// We need the existing ones for this
459
	list ($currently_enabled, $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
460
461
	$config_vars = array(
462
			// Warning system?
463
			'enable' => array('check', 'warning_enable'),
464
	);
465
466
	if (!empty($modSettings['warning_settings']) && $currently_enabled)
467
		$config_vars += array(
468
			'',
469
				array('int', 'warning_watch', 'subtext' => $txt['setting_warning_watch_note'] . ' ' . $txt['zero_to_disable']),
470
				'moderate' => array('int', 'warning_moderate', 'subtext' => $txt['setting_warning_moderate_note'] . ' ' . $txt['zero_to_disable']),
471
				array('int', 'warning_mute', 'subtext' => $txt['setting_warning_mute_note'] . ' ' . $txt['zero_to_disable']),
472
				'rem1' => array('int', 'user_limit', 'subtext' => $txt['setting_user_limit_note']),
473
				'rem2' => array('int', 'warning_decrement', 'subtext' => $txt['setting_warning_decrement_note'] . ' ' . $txt['zero_to_disable']),
474
				array('permissions', 'view_warning'),
475
		);
476
477
	call_integration_hook('integrate_warning_settings', array(&$config_vars));
478
479
	if ($return_config)
480
		return $config_vars;
481
482
	// Cannot use moderation if post moderation is not enabled.
483
	if (!$modSettings['postmod_active'])
484
		unset($config_vars['moderate']);
485
486
	// Will need the utility functions from here.
487
	require_once($sourcedir . '/ManageServer.php');
488
489
	// Saving?
490
	if (isset($_GET['save']))
491
	{
492
		checkSession();
493
494
		// Make sure these don't have an effect.
495
		if (!$currently_enabled && empty($_POST['warning_enable']))
496
		{
497
			$_POST['warning_watch'] = 0;
498
			$_POST['warning_moderate'] = 0;
499
			$_POST['warning_mute'] = 0;
500
		}
501
		// If it was disabled and we're enabling it now, set some sane defaults.
502
		elseif (!$currently_enabled && !empty($_POST['warning_enable']))
503
		{
504
			// Need to add these, these weren't there before...
505
			$vars = array(
506
				'warning_watch' => 10,
507
				'warning_mute' => 60,
508
			);
509
			if ($modSettings['postmod_active'])
510
				$vars['warning_moderate'] = 35;
511
512
			foreach ($vars as $var => $value)
513
			{
514
				$config_vars[] = array('int', $var);
515
				$_POST[$var] = $value;
516
			}
517
		}
518
		else
519
		{
520
			$_POST['warning_watch'] = min($_POST['warning_watch'], 100);
521
			$_POST['warning_moderate'] = $modSettings['postmod_active'] ? min($_POST['warning_moderate'], 100) : 0;
522
			$_POST['warning_mute'] = min($_POST['warning_mute'], 100);
523
		}
524
525
		// We might not have these already depending on how we got here.
526
		$_POST['user_limit'] = isset($_POST['user_limit']) ? (int) $_POST['user_limit'] : $modSettings['user_limit'];
527
		$_POST['warning_decrement'] = isset($_POST['warning_decrement']) ? (int) $_POST['warning_decrement'] : $modSettings['warning_decrement'];
528
529
		// Fix the warning setting array!
530
		$_POST['warning_settings'] = (!empty($_POST['warning_enable']) ? 1 : 0) . ',' . min(100, $_POST['user_limit']) . ',' . min(100, $_POST['warning_decrement']);
531
		$save_vars = $config_vars;
532
		$save_vars[] = array('text', 'warning_settings');
533
		unset($save_vars['enable'], $save_vars['rem1'], $save_vars['rem2']);
534
535
		call_integration_hook('integrate_save_warning_settings', array(&$save_vars));
536
537
		saveDBSettings($save_vars);
538
		$_SESSION['adm-save'] = true;
539
		redirectexit('action=admin;area=warnings');
540
	}
541
542
	// We actually store lots of these together - for efficiency.
543
	list ($modSettings['warning_enable'], $modSettings['user_limit'], $modSettings['warning_decrement']) = explode(',', $modSettings['warning_settings']);
544
545
	$context['sub_template'] = 'show_settings';
546
	$context['post_url'] = $scripturl . '?action=admin;area=warnings;save';
547
	$context['settings_title'] = $txt['warnings'];
548
	$context['page_title'] = $txt['warnings'];
549
550
	$context[$context['admin_menu_name']]['tab_data'] = array(
551
		'title' => $txt['warnings'],
552
		'help' => '',
553
		'description' => $txt['warnings_desc'],
554
	);
555
556
	prepareDBSettingContext($config_vars);
557
}
558
559
/**
560
 * Let's try keep the spam to a minimum ah Thantos?
561
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
562
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<*,string[]|array|s...ray<string|array>>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
563
 */
564
function ModifyAntispamSettings($return_config = false)
565
{
566
	global $txt, $scripturl, $context, $modSettings, $smcFunc, $language, $sourcedir;
567
568
	loadLanguage('Help');
569
	loadLanguage('ManageSettings');
570
571
	// Generate a sample registration image.
572
	$context['use_graphic_library'] = in_array('gd', get_loaded_extensions());
573
	$context['verification_image_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand());
574
575
	$config_vars = array(
576
				array('check', 'reg_verification'),
577
				array('check', 'search_enable_captcha'),
578
				// This, my friend, is a cheat :p
579
				'guest_verify' => array('check', 'guests_require_captcha', 'subtext' => $txt['setting_guests_require_captcha_desc']),
580
				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;}'),
581
			'',
582
			// PM Settings
583
				'pm1' => array('int', 'max_pm_recipients', 'subtext' => $txt['max_pm_recipients_note']),
584
				'pm2' => array('int', 'pm_posts_verification', 'subtext' => $txt['pm_posts_verification_note']),
585
				'pm3' => array('int', 'pm_posts_per_hour', 'subtext' => $txt['pm_posts_per_hour_note']),
586
			// Visual verification.
587
			array('title', 'configure_verification_means'),
588
			array('desc', 'configure_verification_means_desc'),
589
				'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();' : ''),
590
			// reCAPTCHA
591
			array('title', 'recaptcha_configure'),
592
			array('desc', 'recaptcha_configure_desc', 'class' => 'windowbg'),
593
				array('check', 'recaptcha_enabled', 'subtext' => $txt['recaptcha_enable_desc']),
594
				array('text', 'recaptcha_site_key', 'subtext' => $txt['recaptcha_site_key_desc']),
595
				array('text', 'recaptcha_secret_key', 'subtext' => $txt['recaptcha_secret_key_desc']),
596
				array('select', 'recaptcha_theme', array('light' => $txt['recaptcha_theme_light'], 'dark' => $txt['recaptcha_theme_dark'])),
597
			// Clever Thomas, who is looking sheepy now? Not I, the mighty sword swinger did say.
598
			array('title', 'setup_verification_questions'),
599
			array('desc', 'setup_verification_questions_desc'),
600
				array('int', 'qa_verification_number', 'subtext' => $txt['setting_qa_verification_number_desc']),
601
				array('callback', 'question_answer_list'),
602
	);
603
604
	call_integration_hook('integrate_spam_settings', array(&$config_vars));
605
606
	if ($return_config)
607
		return $config_vars;
608
609
	// You need to be an admin to edit settings!
610
	isAllowedTo('admin_forum');
611
612
	// Firstly, figure out what languages we're dealing with, and do a little processing for the form's benefit.
613
	getLanguages();
614
	$context['qa_languages'] = array();
615
	foreach ($context['languages'] as $lang_id => $lang)
616
	{
617
		$lang_id = strtr($lang_id, array('-utf8' => ''));
618
		$lang['name'] = strtr($lang['name'], array('-utf8' => ''));
619
		$context['qa_languages'][$lang_id] = $lang;
620
	}
621
622
	// Secondly, load any questions we currently have.
623
	$context['question_answers'] = array();
624
	$request = $smcFunc['db_query']('', '
625
		SELECT id_question, lngfile, question, answers
626
		FROM {db_prefix}qanda'
627
	);
628
	while ($row = $smcFunc['db_fetch_assoc']($request))
629
	{
630
		$lang = strtr($row['lngfile'], array('-utf8' => ''));
631
		$context['question_answers'][$row['id_question']] = array(
632
			'lngfile' => $lang,
633
			'question' => $row['question'],
634
			'answers' => $smcFunc['json_decode']($row['answers'], true),
635
		);
636
		$context['qa_by_lang'][$lang][] = $row['id_question'];
637
	}
638
639
	if (empty($context['qa_by_lang'][strtr($language, array('-utf8' => ''))]) && !empty($context['question_answers']))
640
	{
641
		if (empty($context['settings_insert_above']))
642
			$context['settings_insert_above'] = '';
643
644
		$context['settings_insert_above'] .= '<div class="noticebox">' . sprintf($txt['question_not_defined'], $context['languages'][$language]['name']) . '</div>';
645
	}
646
647
	// Thirdly, push some JavaScript for the form to make it work.
648
	addInlineJavaScript('
649
	var nextrow = ' . (!empty($context['question_answers']) ? max(array_keys($context['question_answers'])) + 1 : 1) . ';
650
	$(".qa_link a").click(function() {
651
		var id = $(this).parent().attr("id").substring(6);
652
		$("#qa_fs_" + id).show();
653
		$(this).parent().hide();
654
	});
655
	$(".qa_fieldset legend a").click(function() {
656
		var id = $(this).closest("fieldset").attr("id").substring(6);
657
		$("#qa_dt_" + id).show();
658
		$(this).closest("fieldset").hide();
659
	});
660
	$(".qa_add_question a").click(function() {
661
		var id = $(this).closest("fieldset").attr("id").substring(6);
662
		$(\'<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());
663
		nextrow++;
664
	});
665
	function addAnswer(obj)
666
	{
667
		var attr = $(obj).closest("dd").find(".verification_answer:last").attr("name");
668
		$(\'<input type="text" name="\' + attr + \'" value="" size="50" class="input_text verification_answer">\').insertBefore($(obj).closest("div"));
669
		return false;
670
	}
671
	$("#qa_dt_' . strtr($language, array('-utf8' => '')) . ' a").click();', true);
672
673
	// Will need the utility functions from here.
674
	require_once($sourcedir . '/ManageServer.php');
675
676
	// Saving?
677
	if (isset($_GET['save']))
678
	{
679
		checkSession();
680
681
		// Fix PM settings.
682
		$_POST['pm_spam_settings'] = (int) $_POST['max_pm_recipients'] . ',' . (int) $_POST['pm_posts_verification'] . ',' . (int) $_POST['pm_posts_per_hour'];
683
684
		// Hack in guest requiring verification!
685
		if (empty($_POST['posts_require_captcha']) && !empty($_POST['guests_require_captcha']))
686
			$_POST['posts_require_captcha'] = -1;
687
688
		$save_vars = $config_vars;
689
		unset($save_vars['pm1'], $save_vars['pm2'], $save_vars['pm3'], $save_vars['guest_verify']);
690
691
		$save_vars[] = array('text', 'pm_spam_settings');
692
693
		// Handle verification questions.
694
		$changes = array(
695
			'insert' => array(),
696
			'replace' => array(),
697
			'delete' => array(),
698
		);
699
		$qs_per_lang = array();
700
		foreach ($context['qa_languages'] as $lang_id => $dummy)
701
		{
702
			// If we had some questions for this language before, but don't now, delete everything from that language.
703
			if ((!isset($_POST['question'][$lang_id]) || !is_array($_POST['question'][$lang_id])) && !empty($context['qa_by_lang'][$lang_id]))
704
				$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...
705
706
			// Now step through and see if any existing questions no longer exist.
707
			if (!empty($context['qa_by_lang'][$lang_id]))
708
				foreach ($context['qa_by_lang'][$lang_id] as $q_id)
709
					if (empty($_POST['question'][$lang_id][$q_id]))
710
						$changes['delete'][] = $q_id;
711
712
			// Now let's see if there are new questions or ones that need updating.
713
			if (isset($_POST['question'][$lang_id]))
714
			{
715
				foreach ($_POST['question'][$lang_id] as $q_id => $question)
716
				{
717
					// Ignore junky ids.
718
					$q_id = (int) $q_id;
719
					if ($q_id <= 0)
720
						continue;
721
722
					// Check the question isn't empty (because they want to delete it?)
723 View Code Duplication
					if (empty($question) || trim($question) == '')
724
					{
725
						if (isset($context['question_answers'][$q_id]))
726
							$changes['delete'][] = $q_id;
727
						continue;
728
					}
729
					$question = $smcFunc['htmlspecialchars'](trim($question));
730
731
					// Get the answers. Firstly check there actually might be some.
732
					if (!isset($_POST['answer'][$lang_id][$q_id]) || !is_array($_POST['answer'][$lang_id][$q_id]))
733
					{
734
						if (isset($context['question_answers'][$q_id]))
735
							$changes['delete'][] = $q_id;
736
						continue;
737
					}
738
					// Now get them and check that they might be viable.
739
					$answers = array();
740
					foreach ($_POST['answer'][$lang_id][$q_id] as $answer)
741
						if (!empty($answer) && trim($answer) !== '')
742
							$answers[] = $smcFunc['htmlspecialchars'](trim($answer));
743 View Code Duplication
					if (empty($answers))
744
					{
745
						if (isset($context['question_answers'][$q_id]))
746
							$changes['delete'][] = $q_id;
747
						continue;
748
					}
749
					$answers = $smcFunc['json_encode']($answers);
750
751
					// At this point we know we have a question and some answers. What are we doing with it?
752
					if (!isset($context['question_answers'][$q_id]))
753
					{
754
						// New question. Now, we don't want to randomly consume ids, so we'll set those, rather than trusting the browser's supplied ids.
755
						$changes['insert'][] = array($lang_id, $question, $answers);
756
					}
757
					else
758
					{
759
						// It's an existing question. Let's see what's changed, if anything.
760
						if ($lang_id != $context['question_answers'][$q_id]['lngfile'] || $question != $context['question_answers'][$q_id]['question'] || $answers != $context['question_answers'][$q_id]['answers'])
761
							$changes['replace'][$q_id] = array('lngfile' => $lang_id, 'question' => $question, 'answers' => $answers);
762
					}
763
764
					if (!isset($qs_per_lang[$lang_id]))
765
						$qs_per_lang[$lang_id] = 0;
766
					$qs_per_lang[$lang_id]++;
767
				}
768
			}
769
		}
770
771
		// OK, so changes?
772
		if (!empty($changes['delete']))
773
		{
774
			$smcFunc['db_query']('', '
775
				DELETE FROM {db_prefix}qanda
776
				WHERE id_question IN ({array_int:questions})',
777
				array(
778
					'questions' => $changes['delete'],
779
				)
780
			);
781
		}
782
783
		if (!empty($changes['replace']))
784
		{
785
			foreach ($changes['replace'] as $q_id => $question)
786
			{
787
				$smcFunc['db_query']('', '
788
					UPDATE {db_prefix}qanda
789
					SET lngfile = {string:lngfile},
790
						question = {string:question},
791
						answers = {string:answers}
792
					WHERE id_question = {int:id_question}',
793
					array(
794
						'id_question' => $q_id,
795
						'lngfile' => $question['lngfile'],
796
						'question' => $question['question'],
797
						'answers' => $question['answers'],
798
					)
799
				);
800
			}
801
		}
802
803
		if (!empty($changes['insert']))
804
		{
805
			$smcFunc['db_insert']('insert',
806
				'{db_prefix}qanda',
807
				array('lngfile' => 'string-50', 'question' => 'string-255', 'answers' => 'string-65534'),
808
				$changes['insert'],
809
				array('id_question')
810
			);
811
		}
812
813
		// Lastly, the count of messages needs to be no more than the lowest number of questions for any one language.
814
		$count_questions = empty($qs_per_lang) ? 0 : min($qs_per_lang);
815
		if (empty($count_questions) || $_POST['qa_verification_number'] > $count_questions)
816
			$_POST['qa_verification_number'] = $count_questions;
817
818
		call_integration_hook('integrate_save_spam_settings', array(&$save_vars));
819
820
		// Now save.
821
		saveDBSettings($save_vars);
822
		$_SESSION['adm-save'] = true;
823
824
		cache_put_data('verificationQuestions', null, 300);
825
826
		redirectexit('action=admin;area=antispam');
827
	}
828
829
	$character_range = array_merge(range('A', 'H'), array('K', 'M', 'N', 'P', 'R'), range('T', 'Y'));
830
	$_SESSION['visual_verification_code'] = '';
831
	for ($i = 0; $i < 6; $i++)
832
		$_SESSION['visual_verification_code'] .= $character_range[array_rand($character_range)];
833
834
	// Some javascript for CAPTCHA.
835
	$context['settings_post_javascript'] = '';
836
	if ($context['use_graphic_library'])
837
		$context['settings_post_javascript'] .= '
838
		function refreshImages()
839
		{
840
			var imageType = document.getElementById(\'visual_verification_type\').value;
841
			document.getElementById(\'verification_image\').src = \'' . $context['verification_image_href'] . ';type=\' + imageType;
842
		}';
843
844
	// Show the image itself, or text saying we can't.
845
	if ($context['use_graphic_library'])
846
		$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>';
847
	else
848
		$config_vars['vv']['postinput'] = '<br><span class="smalltext">' . $txt['setting_image_verification_nogd'] . '</span>';
849
850
	// Hack for PM spam settings.
851
	list ($modSettings['max_pm_recipients'], $modSettings['pm_posts_verification'], $modSettings['pm_posts_per_hour']) = explode(',', $modSettings['pm_spam_settings']);
852
853
	// Hack for guests requiring verification.
854
	$modSettings['guests_require_captcha'] = !empty($modSettings['posts_require_captcha']);
855
	$modSettings['posts_require_captcha'] = !isset($modSettings['posts_require_captcha']) || $modSettings['posts_require_captcha'] == -1 ? 0 : $modSettings['posts_require_captcha'];
856
857
	// Some minor javascript for the guest post setting.
858
	if ($modSettings['posts_require_captcha'])
859
		$context['settings_post_javascript'] .= '
860
		document.getElementById(\'guests_require_captcha\').disabled = true;';
861
862
	// And everything else.
863
	$context['post_url'] = $scripturl . '?action=admin;area=antispam;save';
864
	$context['settings_title'] = $txt['antispam_Settings'];
865
	$context['page_title'] = $txt['antispam_title'];
866
	$context['sub_template'] = 'show_settings';
867
868
	$context[$context['admin_menu_name']]['tab_data'] = array(
869
		'title' => $txt['antispam_title'],
870
		'description' => $txt['antispam_Settings_desc'],
871
	);
872
873
	prepareDBSettingContext($config_vars);
874
}
875
876
/**
877
 * You'll never guess what this function does...
878
 *
879
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
880
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string[]|string|array|array<*,string>>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
881
 */
882
function ModifySignatureSettings($return_config = false)
883
{
884
	global $context, $txt, $modSettings, $sig_start, $smcFunc, $scripturl;
885
886
	$config_vars = array(
887
			// Are signatures even enabled?
888
			array('check', 'signature_enable'),
889
		'',
890
			// Tweaking settings!
891
			array('int', 'signature_max_length', 'subtext' => $txt['zero_for_no_limit']),
892
			array('int', 'signature_max_lines', 'subtext' => $txt['zero_for_no_limit']),
893
			array('int', 'signature_max_font_size', 'subtext' => $txt['zero_for_no_limit']),
894
			array('check', 'signature_allow_smileys', 'onclick' => 'document.getElementById(\'signature_max_smileys\').disabled = !this.checked;'),
895
			array('int', 'signature_max_smileys', 'subtext' => $txt['zero_for_no_limit']),
896
		'',
897
			// Image settings.
898
			array('int', 'signature_max_images', 'subtext' => $txt['signature_max_images_note']),
899
			array('int', 'signature_max_image_width', 'subtext' => $txt['zero_for_no_limit']),
900
			array('int', 'signature_max_image_height', 'subtext' => $txt['zero_for_no_limit']),
901
		'',
902
			array('bbc', 'signature_bbc'),
903
	);
904
905
	call_integration_hook('integrate_signature_settings', array(&$config_vars));
906
907
	if ($return_config)
908
		return $config_vars;
909
910
	// Setup the template.
911
	$context['page_title'] = $txt['signature_settings'];
912
	$context['sub_template'] = 'show_settings';
913
914
	// Disable the max smileys option if we don't allow smileys at all!
915
	$context['settings_post_javascript'] = 'document.getElementById(\'signature_max_smileys\').disabled = !document.getElementById(\'signature_allow_smileys\').checked;';
916
917
	// Load all the signature settings.
918
	list ($sig_limits, $sig_bbc) = explode(':', $modSettings['signature_settings']);
919
	$sig_limits = explode(',', $sig_limits);
920
	$disabledTags = !empty($sig_bbc) ? explode(',', $sig_bbc) : array();
921
922
	// Applying to ALL signatures?!!
923
	if (isset($_GET['apply']))
924
	{
925
		// Security!
926
		checkSession('get');
927
928
		$sig_start = time();
929
		// This is horrid - but I suppose some people will want the option to do it.
930
		$_GET['step'] = isset($_GET['step']) ? (int) $_GET['step'] : 0;
931
		$done = false;
932
933
		$request = $smcFunc['db_query']('', '
934
			SELECT MAX(id_member)
935
			FROM {db_prefix}members',
936
			array(
937
			)
938
		);
939
		list ($context['max_member']) = $smcFunc['db_fetch_row']($request);
940
		$smcFunc['db_free_result']($request);
941
942
		while (!$done)
943
		{
944
			$changes = array();
945
946
			$request = $smcFunc['db_query']('', '
947
				SELECT id_member, signature
948
				FROM {db_prefix}members
949
				WHERE id_member BETWEEN {int:step} AND {int:step} + 49
950
					AND id_group != {int:admin_group}
951
					AND FIND_IN_SET({int:admin_group}, additional_groups) = 0',
952
				array(
953
					'admin_group' => 1,
954
					'step' => $_GET['step'],
955
				)
956
			);
957
			while ($row = $smcFunc['db_fetch_assoc']($request))
958
			{
959
				// Apply all the rules we can realistically do.
960
				$sig = strtr($row['signature'], array('<br>' => "\n"));
961
962
				// Max characters...
963
				if (!empty($sig_limits[1]))
964
					$sig = $smcFunc['substr']($sig, 0, $sig_limits[1]);
965
				// Max lines...
966
				if (!empty($sig_limits[2]))
967
				{
968
					$count = 0;
969
					for ($i = 0; $i < strlen($sig); $i++)
970
					{
971
						if ($sig[$i] == "\n")
972
						{
973
							$count++;
974
							if ($count >= $sig_limits[2])
975
								$sig = substr($sig, 0, $i) . strtr(substr($sig, $i), array("\n" => ' '));
976
						}
977
					}
978
				}
979
980
				if (!empty($sig_limits[7]) && preg_match_all('~\[size=([\d\.]+)?(px|pt|em|x-large|larger)~i', $sig, $matches) !== false && isset($matches[2]))
981
				{
982
					foreach ($matches[1] as $ind => $size)
983
					{
984
						$limit_broke = 0;
985
						// Attempt to allow all sizes of abuse, so to speak.
986 View Code Duplication
						if ($matches[2][$ind] == 'px' && $size > $sig_limits[7])
987
							$limit_broke = $sig_limits[7] . 'px';
988
						elseif ($matches[2][$ind] == 'pt' && $size > ($sig_limits[7] * 0.75))
989
							$limit_broke = ((int) $sig_limits[7] * 0.75) . 'pt';
990
						elseif ($matches[2][$ind] == 'em' && $size > ((float) $sig_limits[7] / 16))
991
							$limit_broke = ((float) $sig_limits[7] / 16) . 'em';
992
						elseif ($matches[2][$ind] != 'px' && $matches[2][$ind] != 'pt' && $matches[2][$ind] != 'em' && $sig_limits[7] < 18)
993
							$limit_broke = 'large';
994
995
						if ($limit_broke)
996
							$sig = str_replace($matches[0][$ind], '[size=' . $sig_limits[7] . 'px', $sig);
997
					}
998
				}
999
1000
				// Stupid images - this is stupidly, stupidly challenging.
1001
				if ((!empty($sig_limits[3]) || !empty($sig_limits[5]) || !empty($sig_limits[6])))
1002
				{
1003
					$replaces = array();
1004
					$img_count = 0;
1005
					// Get all BBC tags...
1006
					preg_match_all('~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?(\s+width=([\d]+))?\s*\](?:<br>)*([^<">]+?)(?:<br>)*\[/img\]~i', $sig, $matches);
1007
					// ... and all HTML ones.
1008
					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);
1009
					// And stick the HTML in the BBC.
1010 View Code Duplication
					if (!empty($matches2))
1011
					{
1012
						foreach ($matches2[0] as $ind => $dummy)
1013
						{
1014
							$matches[0][] = $matches2[0][$ind];
1015
							$matches[1][] = '';
1016
							$matches[2][] = '';
1017
							$matches[3][] = '';
1018
							$matches[4][] = '';
1019
							$matches[5][] = '';
1020
							$matches[6][] = '';
1021
							$matches[7][] = $matches2[1][$ind];
1022
						}
1023
					}
1024
					// Try to find all the images!
1025
					if (!empty($matches))
1026
					{
1027
						$image_count_holder = array();
1028
						foreach ($matches[0] as $key => $image)
1029
						{
1030
							$width = -1; $height = -1;
0 ignored issues
show
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
1031
							$img_count++;
1032
							// Too many images?
1033
							if (!empty($sig_limits[3]) && $img_count > $sig_limits[3])
1034
							{
1035
								// If we've already had this before we only want to remove the excess.
1036
								if (isset($image_count_holder[$image]))
1037
								{
1038
									$img_offset = -1;
1039
									$rep_img_count = 0;
1040
									while ($img_offset !== false)
1041
									{
1042
										$img_offset = strpos($sig, $image, $img_offset + 1);
1043
										$rep_img_count++;
1044
										if ($rep_img_count > $image_count_holder[$image])
1045
										{
1046
											// Only replace the excess.
1047
											$sig = substr($sig, 0, $img_offset) . str_replace($image, '', substr($sig, $img_offset));
1048
											// Stop looping.
1049
											$img_offset = false;
1050
										}
1051
									}
1052
								}
1053
								else
1054
									$replaces[$image] = '';
1055
1056
								continue;
1057
							}
1058
1059
							// Does it have predefined restraints? Width first.
1060 View Code Duplication
							if ($matches[6][$key])
1061
								$matches[2][$key] = $matches[6][$key];
1062 View Code Duplication
							if ($matches[2][$key] && $sig_limits[5] && $matches[2][$key] > $sig_limits[5])
1063
							{
1064
								$width = $sig_limits[5];
1065
								$matches[4][$key] = $matches[4][$key] * ($width / $matches[2][$key]);
1066
							}
1067
							elseif ($matches[2][$key])
1068
								$width = $matches[2][$key];
1069
							// ... and height.
1070 View Code Duplication
							if ($matches[4][$key] && $sig_limits[6] && $matches[4][$key] > $sig_limits[6])
1071
							{
1072
								$height = $sig_limits[6];
1073
								if ($width != -1)
1074
									$width = $width * ($height / $matches[4][$key]);
1075
							}
1076
							elseif ($matches[4][$key])
1077
								$height = $matches[4][$key];
1078
1079
							// If the dimensions are still not fixed - we need to check the actual image.
1080 View Code Duplication
							if (($width == -1 && $sig_limits[5]) || ($height == -1 && $sig_limits[6]))
1081
							{
1082
								$sizes = url_image_size($matches[7][$key]);
1083
								if (is_array($sizes))
1084
								{
1085
									// Too wide?
1086
									if ($sizes[0] > $sig_limits[5] && $sig_limits[5])
1087
									{
1088
										$width = $sig_limits[5];
1089
										$sizes[1] = $sizes[1] * ($width / $sizes[0]);
1090
									}
1091
									// Too high?
1092
									if ($sizes[1] > $sig_limits[6] && $sig_limits[6])
1093
									{
1094
										$height = $sig_limits[6];
1095
										if ($width == -1)
1096
											$width = $sizes[0];
1097
										$width = $width * ($height / $sizes[1]);
1098
									}
1099
									elseif ($width != -1)
1100
										$height = $sizes[1];
1101
								}
1102
							}
1103
1104
							// Did we come up with some changes? If so remake the string.
1105 View Code Duplication
							if ($width != -1 || $height != -1)
1106
							{
1107
								$replaces[$image] = '[img' . ($width != -1 ? ' width=' . round($width) : '') . ($height != -1 ? ' height=' . round($height) : '') . ']' . $matches[7][$key] . '[/img]';
1108
							}
1109
1110
							// Record that we got one.
1111
							$image_count_holder[$image] = isset($image_count_holder[$image]) ? $image_count_holder[$image] + 1 : 1;
1112
						}
1113
						if (!empty($replaces))
1114
							$sig = str_replace(array_keys($replaces), array_values($replaces), $sig);
1115
					}
1116
				}
1117
				// Try to fix disabled tags.
1118
				if (!empty($disabledTags))
1119
				{
1120
					$sig = preg_replace('~\[(?:' . implode('|', $disabledTags) . ').+?\]~i', '', $sig);
1121
					$sig = preg_replace('~\[/(?:' . implode('|', $disabledTags) . ')\]~i', '', $sig);
1122
				}
1123
1124
				$sig = strtr($sig, array("\n" => '<br>'));
1125
				call_integration_hook('integrate_apply_signature_settings', array(&$sig, $sig_limits, $disabledTags));
1126
				if ($sig != $row['signature'])
1127
					$changes[$row['id_member']] = $sig;
1128
			}
1129
			if ($smcFunc['db_num_rows']($request) == 0)
1130
				$done = true;
1131
			$smcFunc['db_free_result']($request);
1132
1133
			// Do we need to delete what we have?
1134 View Code Duplication
			if (!empty($changes))
1135
			{
1136
				foreach ($changes as $id => $sig)
1137
					$smcFunc['db_query']('', '
1138
						UPDATE {db_prefix}members
1139
						SET signature = {string:signature}
1140
						WHERE id_member = {int:id_member}',
1141
						array(
1142
							'id_member' => $id,
1143
							'signature' => $sig,
1144
						)
1145
					);
1146
			}
1147
1148
			$_GET['step'] += 50;
1149
			if (!$done)
1150
				pauseSignatureApplySettings();
1151
		}
1152
		$settings_applied = true;
1153
	}
1154
1155
	$context['signature_settings'] = array(
1156
		'enable' => isset($sig_limits[0]) ? $sig_limits[0] : 0,
1157
		'max_length' => isset($sig_limits[1]) ? $sig_limits[1] : 0,
1158
		'max_lines' => isset($sig_limits[2]) ? $sig_limits[2] : 0,
1159
		'max_images' => isset($sig_limits[3]) ? $sig_limits[3] : 0,
1160
		'allow_smileys' => isset($sig_limits[4]) && $sig_limits[4] == -1 ? 0 : 1,
1161
		'max_smileys' => isset($sig_limits[4]) && $sig_limits[4] != -1 ? $sig_limits[4] : 0,
1162
		'max_image_width' => isset($sig_limits[5]) ? $sig_limits[5] : 0,
1163
		'max_image_height' => isset($sig_limits[6]) ? $sig_limits[6] : 0,
1164
		'max_font_size' => isset($sig_limits[7]) ? $sig_limits[7] : 0,
1165
	);
1166
1167
	// Temporarily make each setting a modSetting!
1168
	foreach ($context['signature_settings'] as $key => $value)
1169
		$modSettings['signature_' . $key] = $value;
1170
1171
	// Make sure we check the right tags!
1172
	$modSettings['bbc_disabled_signature_bbc'] = $disabledTags;
1173
1174
	// Saving?
1175
	if (isset($_GET['save']))
1176
	{
1177
		checkSession();
1178
1179
		// Clean up the tag stuff!
1180
		$bbcTags = array();
1181
		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...
1182
			$bbcTags[] = $tag['tag'];
1183
1184 View Code Duplication
		if (!isset($_POST['signature_bbc_enabledTags']))
1185
			$_POST['signature_bbc_enabledTags'] = array();
1186
		elseif (!is_array($_POST['signature_bbc_enabledTags']))
1187
			$_POST['signature_bbc_enabledTags'] = array($_POST['signature_bbc_enabledTags']);
1188
1189
		$sig_limits = array();
1190
		foreach ($context['signature_settings'] as $key => $value)
1191
		{
1192
			if ($key == 'allow_smileys')
1193
				continue;
1194
			elseif ($key == 'max_smileys' && empty($_POST['signature_allow_smileys']))
1195
				$sig_limits[] = -1;
1196
			else
1197
				$sig_limits[] = !empty($_POST['signature_' . $key]) ? max(1, (int) $_POST['signature_' . $key]) : 0;
1198
		}
1199
1200
		call_integration_hook('integrate_save_signature_settings', array(&$sig_limits, &$bbcTags));
1201
1202
		$_POST['signature_settings'] = implode(',', $sig_limits) . ':' . implode(',', array_diff($bbcTags, $_POST['signature_bbc_enabledTags']));
1203
1204
		// Even though we have practically no settings let's keep the convention going!
1205
		$save_vars = array();
1206
		$save_vars[] = array('text', 'signature_settings');
1207
1208
		saveDBSettings($save_vars);
1209
		$_SESSION['adm-save'] = true;
1210
		redirectexit('action=admin;area=featuresettings;sa=sig');
1211
	}
1212
1213
	$context['post_url'] = $scripturl . '?action=admin;area=featuresettings;save;sa=sig';
1214
	$context['settings_title'] = $txt['signature_settings'];
1215
1216
	$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>';
1217
1218
	prepareDBSettingContext($config_vars);
1219
}
1220
1221
/**
1222
 * Just pause the signature applying thing.
1223
 */
1224 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...
1225
{
1226
	global $context, $txt, $sig_start;
1227
1228
	// Try get more time...
1229
	@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...
1230
	if (function_exists('apache_reset_timeout'))
1231
		@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...
1232
1233
	// Have we exhausted all the time we allowed?
1234
	if (time() - array_sum(explode(' ', $sig_start)) < 3)
1235
		return;
1236
1237
	$context['continue_get_data'] = '?action=admin;area=featuresettings;sa=sig;apply;step=' . $_GET['step'] . ';' . $context['session_var'] . '=' . $context['session_id'];
1238
	$context['page_title'] = $txt['not_done_title'];
1239
	$context['continue_post_data'] = '';
1240
	$context['continue_countdown'] = '2';
1241
	$context['sub_template'] = 'not_done';
1242
1243
	// Specific stuff to not break this template!
1244
	$context[$context['admin_menu_name']]['current_subsection'] = 'sig';
1245
1246
	// Get the right percent.
1247
	$context['continue_percent'] = round(($_GET['step'] / $context['max_member']) * 100);
1248
1249
	// Never more than 100%!
1250
	$context['continue_percent'] = min($context['continue_percent'], 100);
1251
1252
	obExit();
1253
}
1254
1255
/**
1256
 * Show all the custom profile fields available to the user.
1257
 */
1258
function ShowCustomProfiles()
1259
{
1260
	global $txt, $scripturl, $context;
1261
	global $sourcedir;
1262
1263
	$context['page_title'] = $txt['custom_profile_title'];
1264
	$context['sub_template'] = 'show_custom_profile';
1265
1266
	// What about standard fields they can tweak?
1267
	$standard_fields = array('website', 'personal_text', 'timezone', 'posts', 'warning_status');
1268
	// What fields can't you put on the registration page?
1269
	$context['fields_no_registration'] = array('posts', 'warning_status');
1270
1271
	// Are we saving any standard field changes?
1272
	if (isset($_POST['save']))
1273
	{
1274
		checkSession();
1275
		validateToken('admin-scp');
1276
1277
		// Do the active ones first.
1278
		$disable_fields = array_flip($standard_fields);
1279
		if (!empty($_POST['active']))
1280
		{
1281
			foreach ($_POST['active'] as $value)
1282
				if (isset($disable_fields[$value]))
1283
					unset($disable_fields[$value]);
1284
		}
1285
		// What we have left!
1286
		$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...
1287
1288
		// Things we want to show on registration?
1289
		$reg_fields = array();
1290
		if (!empty($_POST['reg']))
1291
		{
1292
			foreach ($_POST['reg'] as $value)
1293
				if (in_array($value, $standard_fields) && !isset($disable_fields[$value]))
1294
					$reg_fields[] = $value;
1295
		}
1296
		// What we have left!
1297
		$changes['registration_fields'] = empty($reg_fields) ? '' : implode(',', $reg_fields);
1298
1299
		$_SESSION['adm-save'] = true;
1300
		if (!empty($changes))
1301
			updateSettings($changes);
1302
	}
1303
1304
	createToken('admin-scp');
1305
1306
	// Need to know the max order for custom fields
1307
	$context['custFieldsMaxOrder'] = custFieldsMaxOrder();
1308
1309
	require_once($sourcedir . '/Subs-List.php');
1310
1311
	$listOptions = array(
1312
		'id' => 'standard_profile_fields',
1313
		'title' => $txt['standard_profile_title'],
1314
		'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1315
		'get_items' => array(
1316
			'function' => 'list_getProfileFields',
1317
			'params' => array(
1318
				true,
1319
			),
1320
		),
1321
		'columns' => array(
1322
			'field' => array(
1323
				'header' => array(
1324
					'value' => $txt['standard_profile_field'],
1325
				),
1326
				'data' => array(
1327
					'db' => 'label',
1328
					'style' => 'width: 60%;',
1329
				),
1330
			),
1331
			'active' => array(
1332
				'header' => array(
1333
					'value' => $txt['custom_edit_active'],
1334
					'class' => 'centercol',
1335
				),
1336
				'data' => array(
1337 View Code Duplication
					'function' => function ($rowData)
1338
					{
1339
						$isChecked = $rowData['disabled'] ? '' : ' checked';
1340
						$onClickHandler = $rowData['can_show_register'] ? sprintf(' onclick="document.getElementById(\'reg_%1$s\').disabled = !this.checked;"', $rowData['id']) : '';
1341
						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);
1342
					},
1343
					'style' => 'width: 20%;',
1344
					'class' => 'centercol',
1345
				),
1346
			),
1347
			'show_on_registration' => array(
1348
				'header' => array(
1349
					'value' => $txt['custom_edit_registration'],
1350
					'class' => 'centercol',
1351
				),
1352
				'data' => array(
1353 View Code Duplication
					'function' => function ($rowData)
1354
					{
1355
						$isChecked = $rowData['on_register'] && !$rowData['disabled'] ? ' checked' : '';
1356
						$isDisabled = $rowData['can_show_register'] ? '' : ' disabled';
1357
						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);
1358
					},
1359
					'style' => 'width: 20%;',
1360
					'class' => 'centercol',
1361
				),
1362
			),
1363
		),
1364
		'form' => array(
1365
			'href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1366
			'name' => 'standardProfileFields',
1367
			'token' => 'admin-scp',
1368
		),
1369
		'additional_rows' => array(
1370
			array(
1371
				'position' => 'below_table_data',
1372
				'value' => '<input type="submit" name="save" value="' . $txt['save'] . '" class="button_submit">',
1373
			),
1374
		),
1375
	);
1376
	createList($listOptions);
1377
1378
	$listOptions = array(
1379
		'id' => 'custom_profile_fields',
1380
		'title' => $txt['custom_profile_title'],
1381
		'base_href' => $scripturl . '?action=admin;area=featuresettings;sa=profile',
1382
		'default_sort_col' => 'field_order',
1383
		'no_items_label' => $txt['custom_profile_none'],
1384
		'items_per_page' => 25,
1385
		'get_items' => array(
1386
			'function' => 'list_getProfileFields',
1387
			'params' => array(
1388
				false,
1389
			),
1390
		),
1391
		'get_count' => array(
1392
			'function' => 'list_getProfileFieldSize',
1393
		),
1394
		'columns' => array(
1395
			'field_order' => array(
1396
				'header' => array(
1397
					'value' => $txt['custom_profile_fieldorder'],
1398
				),
1399
				'data' => array(
1400
					'function' => function ($rowData) use ($context, $txt, $scripturl)
1401
					{
1402
						$return = '<p class="centertext bold_text">'. $rowData['field_order'] .'<br />';
1403
1404 View Code Duplication
						if ($rowData['field_order'] > 1)
1405
							$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>';
1406
1407 View Code Duplication
						if ($rowData['field_order'] < $context['custFieldsMaxOrder'])
1408
							$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>';
1409
1410
						$return .= '</p>';
1411
1412
						return $return;
1413
					},
1414
					'style' => 'width: 12%;',
1415
				),
1416
				'sort' => array(
1417
					'default' => 'field_order',
1418
					'reverse' => 'field_order DESC',
1419
				),
1420
			),
1421
			'field_name' => array(
1422
				'header' => array(
1423
					'value' => $txt['custom_profile_fieldname'],
1424
				),
1425
				'data' => array(
1426
					'function' => function ($rowData) use ($scripturl)
1427
					{
1428
						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']);
1429
					},
1430
					'style' => 'width: 62%;',
1431
				),
1432
				'sort' => array(
1433
					'default' => 'field_name',
1434
					'reverse' => 'field_name DESC',
1435
				),
1436
			),
1437
			'field_type' => array(
1438
				'header' => array(
1439
					'value' => $txt['custom_profile_fieldtype'],
1440
				),
1441
				'data' => array(
1442
					'function' => function ($rowData) use ($txt)
1443
					{
1444
						$textKey = sprintf('custom_profile_type_%1$s', $rowData['field_type']);
1445
						return isset($txt[$textKey]) ? $txt[$textKey] : $textKey;
1446
					},
1447
					'style' => 'width: 15%;',
1448
					'class' => 'hidden',
1449
				),
1450
				'sort' => array(
1451
					'default' => 'field_type',
1452
					'reverse' => 'field_type DESC',
1453
				),
1454
			),
1455
			'active' => array(
1456
				'header' => array(
1457
					'value' => $txt['custom_profile_active'],
1458
				),
1459
				'data' => array(
1460
					'function' => function ($rowData) use ($txt)
1461
					{
1462
						return $rowData['active'] ? $txt['yes'] : $txt['no'];
1463
					},
1464
					'style' => 'width: 8%;',
1465
					'class' => 'hidden',
1466
				),
1467
				'sort' => array(
1468
					'default' => 'active DESC',
1469
					'reverse' => 'active',
1470
				),
1471
			),
1472
			'placement' => array(
1473
				'header' => array(
1474
					'value' => $txt['custom_profile_placement'],
1475
				),
1476
				'data' => array(
1477
					'function' => function ($rowData)
1478
					{
1479
						global $txt, $context;
1480
1481
						return $txt['custom_profile_placement_' . (empty($rowData['placement']) ? 'standard' : $context['cust_profile_fields_placement'][$rowData['placement']])];
1482
					},
1483
					'style' => 'width: 8%;',
1484
					'class' => 'hidden',
1485
				),
1486
				'sort' => array(
1487
					'default' => 'placement DESC',
1488
					'reverse' => 'placement',
1489
				),
1490
			),
1491
			'show_on_registration' => array(
1492
				'data' => array(
1493
					'sprintf' => array(
1494
						'format' => '<a href="' . $scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=%1$s">' . $txt['modify'] . '</a>',
1495
						'params' => array(
1496
							'id_field' => false,
1497
						),
1498
					),
1499
					'style' => 'width: 15%;',
1500
				),
1501
			),
1502
		),
1503
		'form' => array(
1504
			'href' => $scripturl . '?action=admin;area=featuresettings;sa=profileedit',
1505
			'name' => 'customProfileFields',
1506
		),
1507
		'additional_rows' => array(
1508
			array(
1509
				'position' => 'below_table_data',
1510
				'value' => '<input type="submit" name="new" value="' . $txt['custom_profile_make_new'] . '" class="button_submit">',
1511
			),
1512
		),
1513
	);
1514
	createList($listOptions);
1515
1516
	// There are two different ways we could get to this point. To keep it simple, they both do
1517
	// the same basic thing.
1518
	if (isset($_SESSION['adm-save']))
1519
	{
1520
		$context['saved_successful'] = true;
1521
		unset ($_SESSION['adm-save']);
1522
	}
1523
}
1524
1525
/**
1526
 * Callback for createList().
1527
 * @param int $start The item to start with (used for pagination purposes)
1528
 * @param int $items_per_page The number of items to display per page
1529
 * @param string $sort A string indicating how to sort the results
1530
 * @param bool $standardFields Whether or not to include standard fields as well
1531
 * @return array An array of info about the various profile fields
1532
 */
1533
function list_getProfileFields($start, $items_per_page, $sort, $standardFields)
1534
{
1535
	global $txt, $modSettings, $smcFunc;
1536
1537
	$list = array();
1538
1539
	if ($standardFields)
1540
	{
1541
		$standard_fields = array('website', 'personal_text', 'timezone', 'posts', 'warning_status');
1542
		$fields_no_registration = array('posts', 'warning_status');
1543
		$disabled_fields = isset($modSettings['disabled_profile_fields']) ? explode(',', $modSettings['disabled_profile_fields']) : array();
1544
		$registration_fields = isset($modSettings['registration_fields']) ? explode(',', $modSettings['registration_fields']) : array();
1545
1546
		foreach ($standard_fields as $field)
1547
			$list[] = array(
1548
				'id' => $field,
1549
				'label' => isset($txt['standard_profile_field_' . $field]) ? $txt['standard_profile_field_' . $field] : (isset($txt[$field]) ? $txt[$field] : $field),
1550
				'disabled' => in_array($field, $disabled_fields),
1551
				'on_register' => in_array($field, $registration_fields) && !in_array($field, $fields_no_registration),
1552
				'can_show_register' => !in_array($field, $fields_no_registration),
1553
			);
1554
	}
1555
	else
1556
	{
1557
		// Load all the fields.
1558
		$request = $smcFunc['db_query']('', '
1559
			SELECT id_field, col_name, field_name, field_desc, field_type, field_order, active, placement
1560
			FROM {db_prefix}custom_fields
1561
			ORDER BY {raw:sort}
1562
			LIMIT {int:start}, {int:items_per_page}',
1563
			array(
1564
				'sort' => $sort,
1565
				'start' => $start,
1566
				'items_per_page' => $items_per_page,
1567
			)
1568
		);
1569
		while ($row = $smcFunc['db_fetch_assoc']($request))
1570
			$list[] = $row;
1571
		$smcFunc['db_free_result']($request);
1572
	}
1573
1574
	return $list;
1575
}
1576
1577
/**
1578
 * Callback for createList().
1579
 * @return int The total number of custom profile fields
1580
 */
1581
function list_getProfileFieldSize()
1582
{
1583
	global $smcFunc;
1584
1585
	$request = $smcFunc['db_query']('', '
1586
		SELECT COUNT(*)
1587
		FROM {db_prefix}custom_fields',
1588
		array(
1589
		)
1590
	);
1591
1592
	list ($numProfileFields) = $smcFunc['db_fetch_row']($request);
1593
	$smcFunc['db_free_result']($request);
1594
1595
	return $numProfileFields;
1596
}
1597
1598
/**
1599
 * Edit some profile fields?
1600
 */
1601
function EditCustomProfiles()
1602
{
1603
	global $txt, $scripturl, $context, $smcFunc;
1604
1605
	// Sort out the context!
1606
	$context['fid'] = isset($_GET['fid']) ? (int) $_GET['fid'] : 0;
1607
	$context[$context['admin_menu_name']]['current_subsection'] = 'profile';
1608
	$context['page_title'] = $context['fid'] ? $txt['custom_edit_title'] : $txt['custom_add_title'];
1609
	$context['sub_template'] = 'edit_profile_field';
1610
1611
	// Load the profile language for section names.
1612
	loadLanguage('Profile');
1613
1614
	// There's really only a few places we can go...
1615
	$move_to = array('up', 'down');
1616
1617
	// We need this for both moving and saving so put it right here.
1618
	$order_count = custFieldsMaxOrder();
1619
1620
	if ($context['fid'])
1621
	{
1622
		$request = $smcFunc['db_query']('', '
1623
			SELECT
1624
				id_field, col_name, field_name, field_desc, field_type, field_order, field_length, field_options,
1625
				show_reg, show_display, show_mlist, show_profile, private, active, default_value, can_search,
1626
				bbc, mask, enclose, placement
1627
			FROM {db_prefix}custom_fields
1628
			WHERE id_field = {int:current_field}',
1629
			array(
1630
				'current_field' => $context['fid'],
1631
			)
1632
		);
1633
		$context['field'] = array();
1634
		while ($row = $smcFunc['db_fetch_assoc']($request))
1635
		{
1636
			if ($row['field_type'] == 'textarea')
1637
				@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...
1638
			else
1639
			{
1640
				$rows = 3;
1641
				$cols = 30;
1642
			}
1643
1644
			$context['field'] = array(
1645
				'name' => $row['field_name'],
1646
				'desc' => $row['field_desc'],
1647
				'col_name' => $row['col_name'],
1648
				'profile_area' => $row['show_profile'],
1649
				'reg' => $row['show_reg'],
1650
				'display' => $row['show_display'],
1651
				'mlist' => $row['show_mlist'],
1652
				'type' => $row['field_type'],
1653
				'order' => $row['field_order'],
1654
				'max_length' => $row['field_length'],
1655
				'rows' => $rows,
1656
				'cols' => $cols,
1657
				'bbc' => $row['bbc'] ? true : false,
1658
				'default_check' => $row['field_type'] == 'check' && $row['default_value'] ? true : false,
1659
				'default_select' => $row['field_type'] == 'select' || $row['field_type'] == 'radio' ? $row['default_value'] : '',
1660
				'options' => strlen($row['field_options']) > 1 ? explode(',', $row['field_options']) : array('', '', ''),
1661
				'active' => $row['active'],
1662
				'private' => $row['private'],
1663
				'can_search' => $row['can_search'],
1664
				'mask' => $row['mask'],
1665
				'regex' => substr($row['mask'], 0, 5) == 'regex' ? substr($row['mask'], 5) : '',
1666
				'enclose' => $row['enclose'],
1667
				'placement' => $row['placement'],
1668
			);
1669
		}
1670
		$smcFunc['db_free_result']($request);
1671
	}
1672
1673
	// Setup the default values as needed.
1674
	if (empty($context['field']))
1675
		$context['field'] = array(
1676
			'name' => '',
1677
			'col_name' => '???',
1678
			'desc' => '',
1679
			'profile_area' => 'forumprofile',
1680
			'reg' => false,
1681
			'display' => false,
1682
			'mlist' => false,
1683
			'type' => 'text',
1684
			'order' => 0,
1685
			'max_length' => 255,
1686
			'rows' => 4,
1687
			'cols' => 30,
1688
			'bbc' => false,
1689
			'default_check' => false,
1690
			'default_select' => '',
1691
			'options' => array('', '', ''),
1692
			'active' => true,
1693
			'private' => false,
1694
			'can_search' => false,
1695
			'mask' => 'nohtml',
1696
			'regex' => '',
1697
			'enclose' => '',
1698
			'placement' => 0,
1699
		);
1700
1701
	// Are we moving it?
1702
	if (isset($_GET['move']) && in_array($smcFunc['htmlspecialchars']($_GET['move']), $move_to))
1703
	{
1704
		// Down is the new up.
1705
		$new_order = ($_GET['move'] == 'up' ? ($context['field']['order'] - 1) : ($context['field']['order'] + 1));
1706
1707
		// Is this a valid position?
1708
		if ($new_order <= 0 || $new_order > $order_count)
1709
			redirectexit('action=admin;area=featuresettings;sa=profile'); // @todo implement an error handler
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1710
1711
		// All good, proceed.
1712
		$smcFunc['db_query']('','
1713
			UPDATE {db_prefix}custom_fields
1714
			SET field_order = {int:old_order}
1715
			WHERE field_order = {int:new_order}',
1716
			array(
1717
				'new_order' => $new_order,
1718
				'old_order' => $context['field']['order'],
1719
			)
1720
		);
1721
		$smcFunc['db_query']('','
1722
			UPDATE {db_prefix}custom_fields
1723
			SET field_order = {int:new_order}
1724
			WHERE id_field = {int:id_field}',
1725
			array(
1726
				'new_order' => $new_order,
1727
				'id_field' => $context['fid'],
1728
			)
1729
		);
1730
		redirectexit('action=admin;area=featuresettings;sa=profile'); // @todo perhaps a nice confirmation message, dunno.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1731
	}
1732
1733
	// Are we saving?
1734
	if (isset($_POST['save']))
1735
	{
1736
		checkSession();
1737
		validateToken('admin-ecp');
1738
1739
		// Everyone needs a name - even the (bracket) unknown...
1740
		if (trim($_POST['field_name']) == '')
1741
			redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=need_name');
1742
1743
		// Regex you say?  Do a very basic test to see if the pattern is valid
1744
		if (!empty($_POST['regex']) && @preg_match($_POST['regex'], 'dummy') === false)
1745
			redirectexit($scripturl . '?action=admin;area=featuresettings;sa=profileedit;fid=' . $_GET['fid'] . ';msg=regex_error');
1746
1747
		$_POST['field_name'] = $smcFunc['htmlspecialchars']($_POST['field_name']);
1748
		$_POST['field_desc'] = $smcFunc['htmlspecialchars']($_POST['field_desc']);
1749
1750
		// Checkboxes...
1751
		$show_reg = isset($_POST['reg']) ? (int) $_POST['reg'] : 0;
1752
		$show_display = isset($_POST['display']) ? 1 : 0;
1753
		$show_mlist = isset($_POST['mlist']) ? 1 : 0;
1754
		$bbc = isset($_POST['bbc']) ? 1 : 0;
1755
		$show_profile = $_POST['profile_area'];
1756
		$active = isset($_POST['active']) ? 1 : 0;
1757
		$private = isset($_POST['private']) ? (int) $_POST['private'] : 0;
1758
		$can_search = isset($_POST['can_search']) ? 1 : 0;
1759
1760
		// Some masking stuff...
1761
		$mask = isset($_POST['mask']) ? $_POST['mask'] : '';
1762
		if ($mask == 'regex' && isset($_POST['regex']))
1763
			$mask .= $_POST['regex'];
1764
1765
		$field_length = isset($_POST['max_length']) ? (int) $_POST['max_length'] : 255;
1766
		$enclose = isset($_POST['enclose']) ? $_POST['enclose'] : '';
1767
		$placement = isset($_POST['placement']) ? (int) $_POST['placement'] : 0;
1768
1769
		// Select options?
1770
		$field_options = '';
1771
		$newOptions = array();
1772
		$default = isset($_POST['default_check']) && $_POST['field_type'] == 'check' ? 1 : '';
1773
		if (!empty($_POST['select_option']) && ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio'))
1774
		{
1775
			foreach ($_POST['select_option'] as $k => $v)
1776
			{
1777
				// Clean, clean, clean...
1778
				$v = $smcFunc['htmlspecialchars']($v);
1779
				$v = strtr($v, array(',' => ''));
1780
1781
				// Nada, zip, etc...
1782
				if (trim($v) == '')
1783
					continue;
1784
1785
				// Otherwise, save it boy.
1786
				$field_options .= $v . ',';
1787
				// This is just for working out what happened with old options...
1788
				$newOptions[$k] = $v;
1789
1790
				// Is it default?
1791
				if (isset($_POST['default_select']) && $_POST['default_select'] == $k)
1792
					$default = $v;
1793
			}
1794
			$field_options = substr($field_options, 0, -1);
1795
		}
1796
1797
		// Text area has default has dimensions
1798
		if ($_POST['field_type'] == 'textarea')
1799
			$default = (int) $_POST['rows'] . ',' . (int) $_POST['cols'];
1800
1801
		// Come up with the unique name?
1802
		if (empty($context['fid']))
1803
		{
1804
			$col_name = $smcFunc['substr'](strtr($_POST['field_name'], array(' ' => '')), 0, 6);
1805
			preg_match('~([\w\d_-]+)~', $col_name, $matches);
1806
1807
			// If there is nothing to the name, then let's start out own - for foreign languages etc.
1808
			if (isset($matches[1]))
1809
				$col_name = $initial_col_name = 'cust_' . strtolower($matches[1]);
1810
			else
1811
				$col_name = $initial_col_name = 'cust_' . mt_rand(1, 9999);
1812
1813
			// Make sure this is unique.
1814
			$current_fields = array();
1815
			$request = $smcFunc['db_query']('', '
1816
				SELECT id_field, col_name
1817
				FROM {db_prefix}custom_fields');
1818
			while ($row = $smcFunc['db_fetch_assoc']($request))
1819
				$current_fields[$row['id_field']] = $row['col_name'];
1820
			$smcFunc['db_free_result']($request);
1821
1822
			$unique = false;
1823
			for ($i = 0; !$unique && $i < 9; $i ++)
1824
			{
1825
				if (!in_array($col_name, $current_fields))
1826
					$unique = true;
1827
				else
1828
					$col_name = $initial_col_name . $i;
1829
			}
1830
1831
			// Still not a unique column name? Leave it up to the user, then.
1832
			if (!$unique)
1833
				fatal_lang_error('custom_option_not_unique');
1834
		}
1835
		// Work out what to do with the user data otherwise...
1836
		else
1837
		{
1838
			// Anything going to check or select is pointless keeping - as is anything coming from check!
1839
			if (($_POST['field_type'] == 'check' && $context['field']['type'] != 'check')
1840
				|| (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && $context['field']['type'] != 'select' && $context['field']['type'] != 'radio')
1841
				|| ($context['field']['type'] == 'check' && $_POST['field_type'] != 'check'))
1842
			{
1843
				$smcFunc['db_query']('', '
1844
					DELETE FROM {db_prefix}themes
1845
					WHERE variable = {string:current_column}
1846
						AND id_member > {int:no_member}',
1847
					array(
1848
						'no_member' => 0,
1849
						'current_column' => $context['field']['col_name'],
1850
					)
1851
				);
1852
			}
1853
			// Otherwise - if the select is edited may need to adjust!
1854
			elseif ($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio')
1855
			{
1856
				$optionChanges = array();
1857
				$takenKeys = array();
1858
				// Work out what's changed!
1859
				foreach ($context['field']['options'] as $k => $option)
1860
				{
1861
					if (trim($option) == '')
1862
						continue;
1863
1864
					// Still exists?
1865
					if (in_array($option, $newOptions))
1866
					{
1867
						$takenKeys[] = $k;
1868
						continue;
1869
					}
1870
				}
1871
1872
				// Finally - have we renamed it - or is it really gone?
1873
				foreach ($optionChanges as $k => $option)
1874
				{
1875
					// Just been renamed?
1876
					if (!in_array($k, $takenKeys) && !empty($newOptions[$k]))
1877
						$smcFunc['db_query']('', '
1878
							UPDATE {db_prefix}themes
1879
							SET value = {string:new_value}
1880
							WHERE variable = {string:current_column}
1881
								AND value = {string:old_value}
1882
								AND id_member > {int:no_member}',
1883
							array(
1884
								'no_member' => 0,
1885
								'new_value' => $newOptions[$k],
1886
								'current_column' => $context['field']['col_name'],
1887
								'old_value' => $option,
1888
							)
1889
						);
1890
				}
1891
			}
1892
			// @todo Maybe we should adjust based on new text length limits?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1893
		}
1894
1895
		// Do the insertion/updates.
1896
		if ($context['fid'])
1897
		{
1898
			$smcFunc['db_query']('', '
1899
				UPDATE {db_prefix}custom_fields
1900
				SET
1901
					field_name = {string:field_name}, field_desc = {string:field_desc},
1902
					field_type = {string:field_type}, field_length = {int:field_length},
1903
					field_options = {string:field_options}, show_reg = {int:show_reg},
1904
					show_display = {int:show_display}, show_mlist = {int:show_mlist}, show_profile = {string:show_profile},
1905
					private = {int:private}, active = {int:active}, default_value = {string:default_value},
1906
					can_search = {int:can_search}, bbc = {int:bbc}, mask = {string:mask},
1907
					enclose = {string:enclose}, placement = {int:placement}
1908
				WHERE id_field = {int:current_field}',
1909
				array(
1910
					'field_length' => $field_length,
1911
					'show_reg' => $show_reg,
1912
					'show_display' => $show_display,
1913
					'show_mlist' => $show_mlist,
1914
					'private' => $private,
1915
					'active' => $active,
1916
					'can_search' => $can_search,
1917
					'bbc' => $bbc,
1918
					'current_field' => $context['fid'],
1919
					'field_name' => $_POST['field_name'],
1920
					'field_desc' => $_POST['field_desc'],
1921
					'field_type' => $_POST['field_type'],
1922
					'field_options' => $field_options,
1923
					'show_profile' => $show_profile,
1924
					'default_value' => $default,
1925
					'mask' => $mask,
1926
					'enclose' => $enclose,
1927
					'placement' => $placement,
1928
				)
1929
			);
1930
1931
			// Just clean up any old selects - these are a pain!
1932
			if (($_POST['field_type'] == 'select' || $_POST['field_type'] == 'radio') && !empty($newOptions))
1933
				$smcFunc['db_query']('', '
1934
					DELETE FROM {db_prefix}themes
1935
					WHERE variable = {string:current_column}
1936
						AND value NOT IN ({array_string:new_option_values})
1937
						AND id_member > {int:no_member}',
1938
					array(
1939
						'no_member' => 0,
1940
						'new_option_values' => $newOptions,
1941
						'current_column' => $context['field']['col_name'],
1942
					)
1943
				);
1944
		}
1945
		else
1946
		{
1947
			// Gotta figure it out the order.
1948
			$new_order = $order_count > 1 ? ($order_count + 1) : 1;
1949
1950
			$smcFunc['db_insert']('',
1951
				'{db_prefix}custom_fields',
1952
				array(
1953
					'col_name' => 'string', 'field_name' => 'string', 'field_desc' => 'string',
1954
					'field_type' => 'string', 'field_length' => 'string', 'field_options' => 'string', 'field_order' => 'int',
1955
					'show_reg' => 'int', 'show_display' => 'int', 'show_mlist' => 'int', 'show_profile' => 'string',
1956
					'private' => 'int', 'active' => 'int', 'default_value' => 'string', 'can_search' => 'int',
1957
					'bbc' => 'int', 'mask' => 'string', 'enclose' => 'string', 'placement' => 'int',
1958
				),
1959
				array(
1960
					$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...
1961
					$_POST['field_type'], $field_length, $field_options, $new_order,
1962
					$show_reg, $show_display, $show_mlist, $show_profile,
1963
					$private, $active, $default, $can_search,
1964
					$bbc, $mask, $enclose, $placement,
1965
				),
1966
				array('id_field')
1967
			);
1968
		}
1969
	}
1970
	// Deleting?
1971
	elseif (isset($_POST['delete']) && $context['field']['col_name'])
1972
	{
1973
		checkSession();
1974
		validateToken('admin-ecp');
1975
1976
		// Delete the user data first.
1977
		$smcFunc['db_query']('', '
1978
			DELETE FROM {db_prefix}themes
1979
			WHERE variable = {string:current_column}
1980
				AND id_member > {int:no_member}',
1981
			array(
1982
				'no_member' => 0,
1983
				'current_column' => $context['field']['col_name'],
1984
			)
1985
		);
1986
		// Finally - the field itself is gone!
1987
		$smcFunc['db_query']('', '
1988
			DELETE FROM {db_prefix}custom_fields
1989
			WHERE id_field = {int:current_field}',
1990
			array(
1991
				'current_field' => $context['fid'],
1992
			)
1993
		);
1994
1995
		// Re-arrange the order.
1996
		$smcFunc['db_query']('','
1997
			UPDATE {db_prefix}custom_fields
1998
			SET field_order = field_order - 1
1999
			WHERE field_order > {int:current_order}',
2000
			array(
2001
				'current_order' => $context['field']['order'],
2002
			)
2003
		);
2004
	}
2005
2006
	// Rebuild display cache etc.
2007
	if (isset($_POST['delete']) || isset($_POST['save']))
2008
	{
2009
		checkSession();
2010
2011
		$request = $smcFunc['db_query']('', '
2012
			SELECT col_name, field_name, field_type, field_order, bbc, enclose, placement, show_mlist
2013
			FROM {db_prefix}custom_fields
2014
			WHERE show_display = {int:is_displayed}
2015
				AND active = {int:active}
2016
				AND private != {int:not_owner_only}
2017
				AND private != {int:not_admin_only}
2018
			ORDER BY field_order',
2019
			array(
2020
				'is_displayed' => 1,
2021
				'active' => 1,
2022
				'not_owner_only' => 2,
2023
				'not_admin_only' => 3,
2024
			)
2025
		);
2026
2027
		$fields = array();
2028
		while ($row = $smcFunc['db_fetch_assoc']($request))
2029
		{
2030
			$fields[] = array(
2031
				'col_name' => strtr($row['col_name'], array('|' => '', ';' => '')),
2032
				'title' => strtr($row['field_name'], array('|' => '', ';' => '')),
2033
				'type' => $row['field_type'],
2034
				'order' => $row['field_order'],
2035
				'bbc' => $row['bbc'] ? '1' : '0',
2036
				'placement' => !empty($row['placement']) ? $row['placement'] : '0',
2037
				'enclose' => !empty($row['enclose']) ? $row['enclose'] : '',
2038
				'mlist' => $row['show_mlist'],
2039
			);
2040
		}
2041
		$smcFunc['db_free_result']($request);
2042
2043
		updateSettings(array('displayFields' => $smcFunc['json_encode']($fields)));
2044
		$_SESSION['adm-save'] = true;
2045
		redirectexit('action=admin;area=featuresettings;sa=profile');
2046
	}
2047
2048
	createToken('admin-ecp');
2049
}
2050
2051
/**
2052
 * Returns the maximum field_order value for the custom fields
2053
 * @return int The maximum value of field_order from the custom_fields table
2054
 */
2055
function custFieldsMaxOrder()
2056
{
2057
	global $smcFunc;
2058
2059
	// Gotta know the order limit
2060
	$result = $smcFunc['db_query']('', '
2061
			SELECT MAX(field_order)
2062
			FROM {db_prefix}custom_fields',
2063
			array()
2064
		);
2065
2066
	list ($order_count) = $smcFunc['db_fetch_row']($result);
2067
	$smcFunc['db_free_result']($result);
2068
2069
	return (int) $order_count;
2070
}
2071
2072
/**
2073
 * Allow to edit the settings on the pruning screen.
2074
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
2075
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<*,array<*,string>|...ng[]|string|array>|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
2076
 */
2077
function ModifyLogSettings($return_config = false)
2078
{
2079
	global $txt, $scripturl, $sourcedir, $context, $modSettings;
2080
2081
	// Make sure we understand what's going on.
2082
	loadLanguage('ManageSettings');
2083
2084
	$context['page_title'] = $txt['log_settings'];
2085
2086
	$config_vars = array(
2087
			array('check', 'modlog_enabled', 'help' => 'modlog'),
2088
			array('check', 'adminlog_enabled', 'help' => 'adminlog'),
2089
			array('check', 'userlog_enabled', 'help' => 'userlog'),
2090
			// The error log is a wonderful thing.
2091
			array('title', 'errlog'),
2092
			array('desc', 'error_log_desc'),
2093
			array('check', 'enableErrorLogging'),
2094
			array('check', 'enableErrorQueryLogging'),
2095
			array('check', 'log_ban_hits'),
2096
			// Even do the pruning?
2097
			array('title', 'pruning_title'),
2098
			array('desc', 'pruning_desc'),
2099
			// The array indexes are there so we can remove/change them before saving.
2100
			'pruningOptions' => array('check', 'pruningOptions'),
2101
		'',
2102
			// Various logs that could be pruned.
2103
			array('int', 'pruneErrorLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Error log.
2104
			array('int', 'pruneModLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Moderation log.
2105
			array('int', 'pruneBanLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Ban hit log.
2106
			array('int', 'pruneReportLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Report to moderator log.
2107
			array('int', 'pruneScheduledTaskLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Log of the scheduled tasks and how long they ran.
2108
			array('int', 'pruneSpiderHitLog', 'postinput' => $txt['days_word'], 'subtext' => $txt['zero_to_disable']), // Log of the scheduled tasks and how long they ran.
2109
			// 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.
2110
			// Mod Developers: Do NOT use the pruningOptions master variable for this as SMF Core may overwrite your setting in the future!
2111
	);
2112
2113
	// 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.
2114
	$prune_toggle = array('pruneErrorLog', 'pruneModLog', 'pruneBanLog', 'pruneReportLog', 'pruneScheduledTaskLog', 'pruneSpiderHitLog');
2115
2116
	call_integration_hook('integrate_prune_settings', array(&$config_vars, &$prune_toggle, false));
2117
2118
	$prune_toggle_dt = array();
2119
	foreach ($prune_toggle as $item)
2120
		$prune_toggle_dt[] = 'setting_' . $item;
2121
2122
	if ($return_config)
2123
		return $config_vars;
2124
2125
	addInlineJavaScript('
2126
	function togglePruned()
2127
	{
2128
		var newval = $("#pruningOptions").prop("checked");
2129
		$("#' . implode(', #', $prune_toggle) . '").closest("dd").toggle(newval);
2130
		$("#' . implode(', #', $prune_toggle_dt) . '").closest("dt").toggle(newval);
2131
	};
2132
	togglePruned();
2133
	$("#pruningOptions").click(function() { togglePruned(); });', true);
2134
2135
	// We'll need this in a bit.
2136
	require_once($sourcedir . '/ManageServer.php');
2137
2138
	// Saving?
2139
	if (isset($_GET['save']))
2140
	{
2141
		checkSession();
2142
2143
		// Because of the excitement attached to combining pruning log items, we need to duplicate everything here.
2144
		$savevar = array(
2145
			array('check', 'modlog_enabled'),
2146
			array('check', 'adminlog_enabled'),
2147
			array('check', 'userlog_enabled'),
2148
			array('check', 'enableErrorLogging'),
2149
			array('check', 'enableErrorQueryLogging'),
2150
			array('check', 'log_ban_hits'),
2151
			array('text', 'pruningOptions')
2152
		);
2153
2154
		call_integration_hook('integrate_prune_settings', array(&$savevar, &$prune_toggle, true));
2155
2156
		if (!empty($_POST['pruningOptions']))
2157
		{
2158
			$vals = array();
2159
			foreach ($config_vars as $index => $dummy)
2160
			{
2161
				if (!is_array($dummy) || $index == 'pruningOptions' || !in_array($dummy[1], $prune_toggle))
2162
					continue;
2163
2164
				$vals[] = empty($_POST[$dummy[1]]) || $_POST[$dummy[1]] < 0 ? 0 : (int) $_POST[$dummy[1]];
2165
			}
2166
			$_POST['pruningOptions'] = implode(',', $vals);
2167
		}
2168
		else
2169
			$_POST['pruningOptions'] = '';
2170
2171
		saveDBSettings($savevar);
2172
		$_SESSION['adm-save'] = true;
2173
		redirectexit('action=admin;area=logs;sa=settings');
2174
	}
2175
2176
	$context['post_url'] = $scripturl . '?action=admin;area=logs;save;sa=settings';
2177
	$context['settings_title'] = $txt['log_settings'];
2178
	$context['sub_template'] = 'show_settings';
2179
2180
	// Get the actual values
2181
	if (!empty($modSettings['pruningOptions']))
2182
		@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...
2183
	else
2184
		$modSettings['pruneErrorLog'] = $modSettings['pruneModLog'] = $modSettings['pruneBanLog'] = $modSettings['pruneReportLog'] = $modSettings['pruneScheduledTaskLog'] = $modSettings['pruneSpiderHitLog'] = 0;
2185
2186
	prepareDBSettingContext($config_vars);
2187
}
2188
2189
/**
2190
 * If you have a general mod setting to add stick it here.
2191
 *
2192
 * @param bool $return_config Whether or not to return the config_vars array (used for admin search)
2193
 * @return void|array Returns nothing or returns the $config_vars array if $return_config is true
2194
 */
2195
function ModifyGeneralModSettings($return_config = false)
2196
{
2197
	global $txt, $scripturl, $context;
2198
2199
	$config_vars = array(
2200
		// Mod authors, add any settings UNDER this line. Include a comma at the end of the line and don't remove this statement!!
2201
	);
2202
2203
	// Make it even easier to add new settings.
2204
	call_integration_hook('integrate_general_mod_settings', array(&$config_vars));
2205
2206
	if ($return_config)
2207
		return $config_vars;
2208
2209
	$context['post_url'] = $scripturl . '?action=admin;area=modsettings;save;sa=general';
2210
	$context['settings_title'] = $txt['mods_cat_modifications_misc'];
2211
2212
	// No removing this line you, dirty unwashed mod authors. :p
2213
	if (empty($config_vars))
2214
	{
2215
		$context['settings_save_dont_show'] = true;
2216
		$context['settings_message'] = '<div class="centertext">' . $txt['modification_no_misc_settings'] . '</div>';
2217
2218
		return prepareDBSettingContext($config_vars);
2219
	}
2220
2221
	// Saving?
2222 View Code Duplication
	if (isset($_GET['save']))
2223
	{
2224
		checkSession();
2225
2226
		$save_vars = $config_vars;
2227
2228
		call_integration_hook('integrate_save_general_mod_settings', array(&$save_vars));
2229
2230
		// This line is to help mod authors do a search/add after if you want to add something here. Keyword: FOOT TAPPING SUCKS!
2231
		saveDBSettings($save_vars);
2232
2233
		// This line is to remind mod authors that it's nice to let the users know when something has been saved.
2234
		$_SESSION['adm-save'] = true;
2235
2236
		// This line is to help mod authors do a search/add after if you want to add something here. Keyword: I LOVE TEA!
2237
		redirectexit('action=admin;area=modsettings;sa=general');
2238
	}
2239
2240
	// 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!
2241
	prepareDBSettingContext($config_vars);
2242
}
2243
2244
/**
2245
 * Handles modifying the alerts settings
2246
 */
2247
function ModifyAlertsSettings()
2248
{
2249
	global $context, $modSettings, $sourcedir, $txt;
2250
2251
	// Dummy settings for the template...
2252
	$modSettings['allow_disableAnnounce'] = false;
2253
	$context['user']['is_owner'] = false;
2254
	$context['member'] = array();
2255
	$context['id_member'] = 0;
2256
	$context['menu_item_selected'] = 'alerts';
2257
	$context['token_check'] = 'noti-admin';
2258
2259
	// Specify our action since we'll want to post back here instead of the profile
2260
	$context['action'] = 'action=admin;area=featuresettings;sa=alerts;'. $context['session_var'] .'='. $context['session_id'];
2261
2262
	loadTemplate('Profile');
2263
	loadLanguage('Profile');
2264
2265
	include_once($sourcedir . '/Profile-Modify.php');
2266
	alert_configuration(0);
2267
2268
	$context['page_title'] = $txt['notify_settings'];
2269
2270
	// Override the description
2271
	$context['description'] = $txt['notifications_desc'];
2272
	$context['sub_template'] = 'alert_configuration';
2273
}
2274
2275
?>