Completed
Push — release-2.1 ( e6c696...22bfba )
by Mathias
07:04
created

Load.php ➔ loadTemplate()   C

Complexity

Conditions 19
Paths 84

Size

Total Lines 63
Code Lines 34

Duplication

Lines 8
Ratio 12.7 %

Importance

Changes 0
Metric Value
cc 19
eloc 34
nc 84
nop 3
dl 8
loc 63
rs 6.0112
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file has the hefty job of loading information for the forum.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2016 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 3
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Load the $modSettings array.
21
 */
22
function reloadSettings()
23
{
24
	global $modSettings, $boarddir, $smcFunc, $txt, $db_character_set;
25
	global $cache_enable, $sourcedir, $context;
26
27
	// Most database systems have not set UTF-8 as their default input charset.
28
	if (!empty($db_character_set))
29
		$smcFunc['db_query']('', '
30
			SET NAMES {string:db_character_set}',
31
			array(
32
				'db_character_set' => $db_character_set,
33
			)
34
		);
35
36
	// We need some caching support, maybe.
37
	loadCacheAccelerator();
38
39
	// Try to load it from the cache first; it'll never get cached if the setting is off.
40
	if (($modSettings = cache_get_data('modSettings', 90)) == null)
41
	{
42
		$request = $smcFunc['db_query']('', '
43
			SELECT variable, value
44
			FROM {db_prefix}settings',
45
			array(
46
			)
47
		);
48
		$modSettings = array();
49
		if (!$request)
50
			display_db_error();
51 View Code Duplication
		while ($row = $smcFunc['db_fetch_row']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
52
			$modSettings[$row[0]] = $row[1];
53
		$smcFunc['db_free_result']($request);
54
55
		// Do a few things to protect against missing settings or settings with invalid values...
56 View Code Duplication
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
57
			$modSettings['defaultMaxTopics'] = 20;
58 View Code Duplication
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
59
			$modSettings['defaultMaxMessages'] = 15;
60 View Code Duplication
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
61
			$modSettings['defaultMaxMembers'] = 30;
62 View Code Duplication
		if (empty($modSettings['defaultMaxListItems']) || $modSettings['defaultMaxListItems'] <= 0 || $modSettings['defaultMaxListItems'] > 999)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
63
			$modSettings['defaultMaxListItems'] = 15;
64
65
		if (!is_array($modSettings['attachmentUploadDir']))
66
			$modSettings['attachmentUploadDir'] = smf_json_decode($modSettings['attachmentUploadDir'], true);
67
68
		if (!empty($cache_enable))
69
			cache_put_data('modSettings', $modSettings, 90);
70
	}
71
72
	$modSettings['cache_enable'] = $cache_enable;
73
74
	// UTF-8 ?
75
	$utf8 = (empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8';
76
77
	// Set a list of common functions.
78
	$ent_list = empty($modSettings['disableEntityCheck']) ? '&(#\d{1,7}|quot|amp|lt|gt|nbsp);' : '&(#021|quot|amp|lt|gt|nbsp);';
79
	$ent_check = empty($modSettings['disableEntityCheck']) ? function ($string)
80
		{
81
			$string = preg_replace_callback('~(&#(\d{1,7}|x[0-9a-fA-F]{1,6});)~', 'entity_fix__callback', $string);
82
			return $string;
83
		} : function ($string)
84
		{
85
			return $string;
86
		};
87
	$fix_utf8mb4 = function ($string) use ($utf8)
88
	{
89
		if (!$utf8)
90
			return $string;
91
92
		$i = 0;
93
		$len = strlen($string);
94
		$new_string = '';
95
		while ($i < $len)
96
		{
97
			$ord = ord($string[$i]);
98
			if ($ord < 128)
99
			{
100
				$new_string .= $string[$i];
101
				$i++;
102
			}
103 View Code Duplication
			elseif ($ord < 224)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
104
			{
105
				$new_string .= $string[$i] . $string[$i+1];
106
				$i += 2;
107
			}
108 View Code Duplication
			elseif ($ord < 240)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
109
			{
110
				$new_string .= $string[$i] . $string[$i+1] . $string[$i+2];
111
				$i += 3;
112
			}
113
			elseif ($ord < 248)
114
			{
115
				// Magic happens.
116
				$val = (ord($string[$i]) & 0x07) << 18;
117
				$val += (ord($string[$i+1]) & 0x3F) << 12;
118
				$val += (ord($string[$i+2]) & 0x3F) << 6;
119
				$val += (ord($string[$i+3]) & 0x3F);
120
				$new_string .= '&#' . $val . ';';
121
				$i += 4;
122
			}
123
		}
124
		return $new_string;
125
	};
126
127
	// Preg_replace space characters depend on the character set in use
128
	$space_chars = $utf8 ? '\x{A0}\x{AD}\x{2000}-\x{200F}\x{201F}\x{202F}\x{3000}\x{FEFF}' : '\x00-\x08\x0B\x0C\x0E-\x19\xA0';
129
130
	// global array of anonymous helper functions, used mostly to properly handle multi byte strings
131
	$smcFunc += array(
132
		'entity_fix' => function ($string)
133
		{
134
			$num = $string[0] === 'x' ? hexdec(substr($string, 1)) : (int) $string;
135
			return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) || $num === 0x202E || $num === 0x202D ? '' : '&#' . $num . ';';
136
		},
137
		'htmlspecialchars' => function ($string, $quote_style = ENT_COMPAT, $charset = 'ISO-8859-1') use ($ent_check, $utf8, $fix_utf8mb4)
138
		{
139
			return $fix_utf8mb4($ent_check(htmlspecialchars($string, $quote_style, $utf8 ? 'UTF-8' : $charset)));
140
		},
141
		'htmltrim' => function ($string) use ($utf8, $space_chars, $ent_check)
142
		{
143
			return preg_replace('~^(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+|(?:[ \t\n\r\x0B\x00' . $space_chars . ']|&nbsp;)+$~' . ($utf8 ? 'u' : ''), '', $ent_check($string));
144
		},
145
		'strlen' => function ($string) use ($ent_list, $utf8, $ent_check)
146
		{
147
			return strlen(preg_replace('~' . $ent_list . ($utf8 ? '|.~u' : '~'), '_', $ent_check($string)));
148
		},
149
		'strpos' => function ($haystack, $needle, $offset = 0) use ($utf8, $ent_check, $modSettings)
150
		{
151
			$haystack_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : ''), $ent_check($haystack), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
152
153
			if (strlen($needle) === 1)
154
			{
155
				$result = array_search($needle, array_slice($haystack_arr, $offset));
156
				return is_int($result) ? $result + $offset : false;
157
			}
158
			else
159
			{
160
				$needle_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '', $ent_check($needle), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
161
				$needle_size = count($needle_arr);
162
163
				$result = array_search($needle_arr[0], array_slice($haystack_arr, $offset));
164
				while ((int) $result === $result)
165
				{
166
					$offset += $result;
167
					if (array_slice($haystack_arr, $offset, $needle_size) === $needle_arr)
168
						return $offset;
169
					$result = array_search($needle_arr[0], array_slice($haystack_arr, ++$offset));
170
				}
171
				return false;
172
			}
173
		},
174
		'substr' => function ($string, $start, $length = null) use ($utf8, $ent_check, $modSettings)
175
		{
176
			$ent_arr = preg_split('~(&#' . (empty($modSettings['disableEntityCheck']) ? '\d{1,7}' : '021') . ';|&quot;|&amp;|&lt;|&gt;|&nbsp;|.)~' . ($utf8 ? 'u' : '') . '', $ent_check($string), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
177
			return $length === null ? implode('', array_slice($ent_arr, $start)) : implode('', array_slice($ent_arr, $start, $length));
178
		},
179 View Code Duplication
		'strtolower' => $utf8 ? function ($string) use ($sourcedir)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
180
		{
181
			if (!function_exists('mb_strtolower'))
182
			{
183
				require_once($sourcedir . '/Subs-Charset.php');
184
				return utf8_strtolower($string);
185
			}
186
187
			return mb_strtolower($string, 'UTF-8');
188
		} : 'strtolower',
189
		'strtoupper' => $utf8 ? function ($string)
190
		{
191
			global $sourcedir;
192
193
			if (!function_exists('mb_strtolower'))
194
			{
195
				require_once($sourcedir . '/Subs-Charset.php');
196
				return utf8_strtoupper($string);
197
			}
198
199
			return mb_strtoupper($string, 'UTF-8');
200
		} : 'strtoupper',
201
		'truncate' => function($string, $length) use ($utf8, $ent_check, $ent_list, &$smcFunc)
202
		{
203
			$string = $ent_check($string);
204
			preg_match('~^(' . $ent_list . '|.){' . $smcFunc['strlen'](substr($string, 0, $length)) . '}~'.  ($utf8 ? 'u' : ''), $string, $matches);
205
			$string = $matches[0];
206
			while (strlen($string) > $length)
207
				$string = preg_replace('~(?:' . $ent_list . '|.)$~'.  ($utf8 ? 'u' : ''), '', $string);
208
			return $string;
209
		},
210
		'ucfirst' => $utf8 ? function ($string) use (&$smcFunc)
211
		{
212
			return $smcFunc['strtoupper']($smcFunc['substr']($string, 0, 1)) . $smcFunc['substr']($string, 1);
213
		} : 'ucfirst',
214
		'ucwords' => $utf8 ? function ($string) use (&$smcFunc)
215
		{
216
			$words = preg_split('~([\s\r\n\t]+)~', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
217
			for ($i = 0, $n = count($words); $i < $n; $i += 2)
218
				$words[$i] = $smcFunc['ucfirst']($words[$i]);
219
			return implode('', $words);
220
		} : 'ucwords',
221
	);
222
223
	// Setting the timezone is a requirement for some functions.
224
	if (isset($modSettings['default_timezone']) && in_array($modSettings['default_timezone'], timezone_identifiers_list()))
225
		date_default_timezone_set($modSettings['default_timezone']);
226
	else
227
	{
228
		// Get PHP's default timezone, if set
229
		$ini_tz = ini_get('date.timezone');
230
		if (!empty($ini_tz))
231
			$modSettings['default_timezone'] = $ini_tz;
232
		else
233
			$modSettings['default_timezone'] = '';
234
235
		// If date.timezone is unset, invalid, or just plain weird, make a best guess
236 View Code Duplication
		if (!in_array($modSettings['default_timezone'], timezone_identifiers_list()))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
237
		{	
238
			$server_offset = @mktime(0, 0, 0, 1, 1, 1970);
239
			$modSettings['default_timezone'] = timezone_name_from_abbr('', $server_offset, 0);
240
		}
241
242
		date_default_timezone_set($modSettings['default_timezone']);
243
	}
244
245
	// Check the load averages?
246
	if (!empty($modSettings['loadavg_enable']))
247
	{
248
		if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null)
249
		{
250
			$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
251
			if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0)
252
				$modSettings['load_average'] = (float) $matches[1];
253 View Code Duplication
			elseif (($modSettings['load_average'] = @`uptime`) != null && preg_match('~load average[s]?: (\d+\.\d+), (\d+\.\d+), (\d+\.\d+)~i', $modSettings['load_average'], $matches) != 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
254
				$modSettings['load_average'] = (float) $matches[1];
255
			else
256
				unset($modSettings['load_average']);
257
258
			if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
259
				cache_put_data('loadavg', $modSettings['load_average'], 90);
260
		}
261
262
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
263
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
264
265
		if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
266
			display_loadavg_error();
267
	}
268
269
	// Is post moderation alive and well? Everywhere else assumes this has been defined, so let's make sure it is.
270
	$modSettings['postmod_active'] = !empty($modSettings['postmod_active']);
271
272
	// Here to justify the name of this function. :P
273
	// It should be added to the install and upgrade scripts.
274
	// But since the converters need to be updated also. This is easier.
275
	if (empty($modSettings['currentAttachmentUploadDir']))
276
	{
277
		updateSettings(array(
278
			'attachmentUploadDir' => json_encode(array(1 => $modSettings['attachmentUploadDir'])),
279
			'currentAttachmentUploadDir' => 1,
280
		));
281
	}
282
283
	// Integration is cool.
284
	if (defined('SMF_INTEGRATION_SETTINGS'))
285
	{
286
		$integration_settings = smf_json_decode(SMF_INTEGRATION_SETTINGS, true);
287
		foreach ($integration_settings as $hook => $function)
288
			add_integration_function($hook, $function, '', false);
0 ignored issues
show
Documentation introduced by
'' is of type string, but the function expects a boolean.

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...
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...
289
	}
290
291
	// Any files to pre include?
292 View Code Duplication
	if (!empty($modSettings['integrate_pre_include']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
293
	{
294
		$pre_includes = explode(',', $modSettings['integrate_pre_include']);
295
		foreach ($pre_includes as $include)
296
		{
297
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
298
			if (file_exists($include))
299
				require_once($include);
300
		}
301
	}
302
303
	// This determines the server... not used in many places, except for login fixing.
304
	$context['server'] = array(
305
		'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false,
306
		'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false,
307
		'is_litespeed' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false,
308
		'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false,
309
		'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false,
310
		'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false,
311
		'is_windows' => strpos(PHP_OS, 'WIN') === 0,
312
		'iso_case_folding' => ord(strtolower(chr(138))) === 154,
313
	);
314
	// A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
315
	$context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis'];
316
317
	// Define a list of icons used across multiple places.
318
	$context['stable_icons'] = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'poll', 'moved', 'recycled', 'clip');
319
320
	// Define an array for custom profile fields placements.
321
	$context['cust_profile_fields_placement'] = array(
322
		'standard',
323
		'icons',
324
		'above_signature',
325
		'below_signature',
326
		'below_avatar',
327
		'above_member',
328
		'bottom_poster',
329
	);
330
331
	// Define an array for content-related <meta> elements (e.g. description, keywords, Open Graph) for the HTML head.
332
	$context['meta_tags'] = array();
333
334
	// Define an array of allowed HTML tags.
335
	$context['allowed_html_tags'] = array(
336
		'<img>',
337
		'<div>',
338
	);
339
340
	// These are the only valid image types for SMF, by default anyway.
341
	$context['validImageTypes'] = array(
342
		1 => 'gif',
343
		2 => 'jpeg',
344
		3 => 'png',
345
		5 => 'psd',
346
		6 => 'bmp',
347
		7 => 'tiff',
348
		8 => 'tiff',
349
		9 => 'jpeg',
350
		14 => 'iff'
351
	);
352
353
	// Define a list of allowed tags for descriptions.
354
	$context['description_allowed_tags'] = array('abbr', 'anchor', 'b', 'center', 'color', 'font', 'hr', 'i', 'img', 'iurl', 'left', 'li', 'list', 'ltr', 'pre', 'right', 's', 'sub', 'sup', 'table', 'td', 'tr', 'u', 'url',);
355
356
	// Get an error count, if necessary
357
	if (!isset($context['num_errors']))
358
	{
359
		$query = $smcFunc['db_query']('', '
360
			SELECT COUNT(id_error)
361
			FROM {db_prefix}log_errors',
362
			array()
363
		);
364
365
		list($context['num_errors']) = $smcFunc['db_fetch_row']($query);
366
		$smcFunc['db_free_result']($query);
367
	}
368
369
	// Call pre load integration functions.
370
	call_integration_hook('integrate_pre_load');
371
}
372
373
/**
374
 * Load all the important user information.
375
 * What it does:
376
 * 	- sets up the $user_info array
377
 * 	- assigns $user_info['query_wanna_see_board'] for what boards the user can see.
378
 * 	- first checks for cookie or integration validation.
379
 * 	- uses the current session if no integration function or cookie is found.
380
 * 	- checks password length, if member is activated and the login span isn't over.
381
 * 		- if validation fails for the user, $id_member is set to 0.
382
 * 		- updates the last visit time when needed.
383
 */
384
function loadUserSettings()
385
{
386
	global $modSettings, $user_settings, $sourcedir, $smcFunc;
387
	global $cookiename, $user_info, $language, $context, $image_proxy_enabled, $image_proxy_secret, $boardurl;
388
389
	// Check first the integration, then the cookie, and last the session.
390
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
391
	{
392
		$id_member = 0;
393
		foreach ($integration_ids as $integration_id)
394
		{
395
			$integration_id = (int) $integration_id;
396
			if ($integration_id > 0)
397
			{
398
				$id_member = $integration_id;
399
				$already_verified = true;
400
				break;
401
			}
402
		}
403
	}
404
	else
405
		$id_member = 0;
406
407
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
408
	{
409
		$cookie_data = smf_json_decode($_COOKIE[$cookiename], true, false);
410
411
		if (empty($cookie_data))
412
			$cookie_data = safe_unserialize($_COOKIE[$cookiename]);
413
414
		list ($id_member, $password) = $cookie_data;
415
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
416
	}
417
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
418
	{
419
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
420
		$cookie_data = smf_json_decode($_SESSION['login_' . $cookiename]);
421
422
		if (empty($cookie_data))
423
			$cookie_data = safe_unserialize($_SESSION['login_' . $cookiename]);
424
425
		list ($id_member, $password, $login_span) = $cookie_data;
426
		$id_member = !empty($id_member) && strlen($password) == 128 && $login_span > time() ? (int) $id_member : 0;
427
	}
428
429
	// Only load this stuff if the user isn't a guest.
430
	if ($id_member != 0)
431
	{
432
		// Is the member data cached?
433
		if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
434
		{
435
			$request = $smcFunc['db_query']('', '
436
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
437
				FROM {db_prefix}members AS mem
438
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
439
				WHERE mem.id_member = {int:id_member}
440
				LIMIT 1',
441
				array(
442
					'id_member' => $id_member,
443
				)
444
			);
445
			$user_settings = $smcFunc['db_fetch_assoc']($request);
446
			$smcFunc['db_free_result']($request);
447
448 View Code Duplication
			if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($user_settings['avatar'], 'http://') !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
449
				$user_settings['avatar'] = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($user_settings['avatar']) . '&hash=' . md5($user_settings['avatar'] . $image_proxy_secret);
450
451
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
452
				cache_put_data('user_settings-' . $id_member, $user_settings, 60);
453
		}
454
455
		// Did we find 'im?  If not, junk it.
456
		if (!empty($user_settings))
457
		{
458
			// As much as the password should be right, we can assume the integration set things up.
459
			if (!empty($already_verified) && $already_verified === true)
460
				$check = true;
461
			// SHA-512 hash should be 128 characters long.
462
			elseif (strlen($password) == 128)
0 ignored issues
show
Bug introduced by
The variable $password 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...
463
				$check = hash_salt($user_settings['passwd'], $user_settings['password_salt']) == $password;
464
			else
465
				$check = false;
466
467
			// Wrong password or not activated - either way, you're going nowhere.
468
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
469
		}
470
		else
471
			$id_member = 0;
472
473
		// If we no longer have the member maybe they're being all hackey, stop brute force!
474
		if (!$id_member)
475
		{
476
			require_once($sourcedir . '/LogInOut.php');
477
			validatePasswordFlood(!empty($user_settings['id_member']) ? $user_settings['id_member'] : $id_member, !empty($user_settings['passwd_flood']) ? $user_settings['passwd_flood'] : false, $id_member != 0);
478
		}
479
		// Validate for Two Factor Authentication
480
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('login2', 'logintfa'))))
481
		{
482
			$tfacookie = $cookiename . '_tfa';
483
			$tfasecret = null;
484
485
			$verified = call_integration_hook('integrate_verify_tfa', array($id_member, $user_settings));
486
487
			if (empty($verified) || !in_array(true, $verified))
488
			{
489
				if (!empty($_COOKIE[$tfacookie]))
490
				{
491
					$tfa_data = smf_json_decode($_COOKIE[$tfacookie]);
492
493
					if (is_null($tfa_data))
494
						$tfa_data = safe_unserialize($_COOKIE[$tfacookie]);
495
496
					list ($tfamember, $tfasecret) = $tfa_data;
497
498
					if ((int) $tfamember != $id_member)
499
						$tfasecret = null;
500
				}
501
502
				if (empty($tfasecret) || hash_salt($user_settings['tfa_backup'], $user_settings['password_salt']) != $tfasecret)
503
				{
504
					$id_member = 0;
505
					redirectexit('action=logintfa');
506
				}
507
			}
508
		}
509
		// When authenticating their two factor code, make sure to reset their ID for security
510
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && $_REQUEST['action'] == 'logintfa')
511
		{
512
			$id_member = 0;
513
			$context['tfa_member'] = $user_settings;
514
			$user_settings = array();
515
		}
516
		// Are we forcing 2FA? Need to check if the user groups actually require 2FA
517
		elseif (!empty($modSettings['tfa_mode']) && $modSettings['tfa_mode'] >= 2 && $id_member && empty($user_settings['tfa_secret']))
518
		{
519
			if ($modSettings['tfa_mode'] == 2) //only do this if we are just forcing SOME membergroups
520
			{
521
				//Build an array of ALL user membergroups.
522
				$full_groups = array($user_settings['id_group']);
523
				if (!empty($user_settings['additional_groups']))
524
				{
525
					$full_groups = array_merge($full_groups, explode(',', $user_settings['additional_groups']));
526
					$full_groups = array_unique($full_groups); //duplicates, maybe?
527
				}
528
529
				//Find out if any group requires 2FA
530
				$request = $smcFunc['db_query']('', '
531
					SELECT COUNT(id_group) AS total
532
					FROM {db_prefix}membergroups
533
					WHERE tfa_required = {int:tfa_required}
534
						AND id_group IN ({array_int:full_groups})',
535
					array(
536
						'tfa_required' => 1,
537
						'full_groups' => $full_groups,
538
					)
539
				);
540
				$row = $smcFunc['db_fetch_assoc']($request);
541
				$smcFunc['db_free_result']($request);
542
			}
543
			else
544
				$row['total'] = 1; //simplifies logics in the next "if"
0 ignored issues
show
Coding Style Comprehensibility introduced by
$row was never initialized. Although not strictly required by PHP, it is generally a good practice to add $row = 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...
545
546
			$area = !empty($_REQUEST['area']) ? $_REQUEST['area'] : '';
547
			$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
548
549
			if ($row['total'] > 0 && !in_array($action, array('profile', 'logout')) || ($action == 'profile' && $area != 'tfasetup'))
550
				redirectexit('action=profile;area=tfasetup;forced');
551
		}
552
	}
553
554
	// Found 'im, let's set up the variables.
555
	if ($id_member != 0)
556
	{
557
		// Let's not update the last visit time in these cases...
558
		// 1. SSI doesn't count as visiting the forum.
559
		// 2. RSS feeds and XMLHTTP requests don't count either.
560
		// 3. If it was set within this session, no need to set it again.
561
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
562
		// 5. We're still logging in or authenticating
563
		if (SMF != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || !in_array($_REQUEST['action'], array('.xml', 'login2', 'logintfa'))) && empty($_SESSION['id_msg_last_visit']) && (empty($modSettings['cache_enable']) || ($_SESSION['id_msg_last_visit'] = cache_get_data('user_last_visit-' . $id_member, 5 * 3600)) === null))
564
		{
565
			// @todo can this be cached?
566
			// Do a quick query to make sure this isn't a mistake.
567
			$result = $smcFunc['db_query']('', '
568
				SELECT poster_time
569
				FROM {db_prefix}messages
570
				WHERE id_msg = {int:id_msg}
571
				LIMIT 1',
572
				array(
573
					'id_msg' => $user_settings['id_msg_last_visit'],
574
				)
575
			);
576
			list ($visitTime) = $smcFunc['db_fetch_row']($result);
577
			$smcFunc['db_free_result']($result);
578
579
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
580
581
			// If it was *at least* five hours ago...
582
			if ($visitTime < time() - 5 * 3600)
583
			{
584
				updateMemberData($id_member, array('id_msg_last_visit' => (int) $modSettings['maxMsgID'], 'last_login' => time(), 'member_ip' => $_SERVER['REMOTE_ADDR'], 'member_ip2' => $_SERVER['BAN_CHECK_IP']));
585
				$user_settings['last_login'] = time();
586
587
				if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
588
					cache_put_data('user_settings-' . $id_member, $user_settings, 60);
589
590
				if (!empty($modSettings['cache_enable']))
591
					cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
592
			}
593
		}
594
		elseif (empty($_SESSION['id_msg_last_visit']))
595
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
596
597
		$username = $user_settings['member_name'];
598
599
		if (empty($user_settings['additional_groups']))
600
			$user_info = array(
601
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
602
			);
603 View Code Duplication
		else
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
604
			$user_info = array(
605
				'groups' => array_merge(
606
					array($user_settings['id_group'], $user_settings['id_post_group']),
607
					explode(',', $user_settings['additional_groups'])
608
				)
609
			);
610
611
		// Because history has proven that it is possible for groups to go bad - clean up in case.
612
		foreach ($user_info['groups'] as $k => $v)
613
			$user_info['groups'][$k] = (int) $v;
614
615
		// This is a logged in user, so definitely not a spider.
616
		$user_info['possibly_robot'] = false;
617
618
		// Figure out the new time offset.
619
		if (!empty($user_settings['timezone']))
620
		{
621
			// Get the offsets from UTC for the server, then for the user.
622
			$tz_system = new DateTimeZone(@date_default_timezone_get());
623
			$tz_user = new DateTimeZone($user_settings['timezone']);
624
			$time_system = new DateTime('now', $tz_system);
625
			$time_user = new DateTime('now', $tz_user);
626
			$user_info['time_offset'] = ($tz_user->getOffset($time_user) - $tz_system->getOffset($time_system)) / 3600;
627
		}
628
		else
629
		{
630
			// !!! Compatibility.
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
631
			$user_info['time_offset'] = empty($user_settings['time_offset']) ? 0 :$user_settings['time_offset'];
632
		}
633
	}
634
	// If the user is a guest, initialize all the critical user settings.
635
	else
636
	{
637
		// This is what a guest's variables should be.
638
		$username = '';
639
		$user_info = array('groups' => array(-1));
640
		$user_settings = array();
641
642
		if (isset($_COOKIE[$cookiename]) && empty($context['tfa_member']))
643
			$_COOKIE[$cookiename] = '';
644
645
		// Expire the 2FA cookie
646
		if (isset($_COOKIE[$cookiename . '_tfa']) && empty($context['tfa_member']))
647
		{
648
			$tfa_data = smf_json_decode($_COOKIE[$cookiename . '_tfa'], true);
649
650
			if (is_null($tfa_data))
651
				$tfa_data = safe_unserialize($_COOKIE[$cookiename . '_tfa']);
652
653
			list ($id, $user, $exp, $state, $preserve) = $tfa_data;
0 ignored issues
show
Unused Code introduced by
The assignment to $id is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $user is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $state is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
654
655
			if (!$preserve || time() > $exp)
656
			{
657
				$_COOKIE[$cookiename . '_tfa'] = '';
658
				setTFACookie(-3600, 0, '');
659
			}
660
		}
661
662
		// Create a login token if it doesn't exist yet.
663
		if (!isset($_SESSION['token']['post-login']))
664
			createToken('login');
665
		else
666
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
667
668
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
669
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
670
		{
671
			require_once($sourcedir . '/ManageSearchEngines.php');
672
			$user_info['possibly_robot'] = SpiderCheck();
673
		}
674
		elseif (!empty($modSettings['spider_mode']))
675
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
676
		// If we haven't turned on proper spider hunts then have a guess!
677
		else
678
		{
679
			$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
680
			$user_info['possibly_robot'] = (strpos($_SERVER['HTTP_USER_AGENT'], 'Mozilla') === false && strpos($_SERVER['HTTP_USER_AGENT'], 'Opera') === false) || strpos($ci_user_agent, 'googlebot') !== false || strpos($ci_user_agent, 'slurp') !== false || strpos($ci_user_agent, 'crawl') !== false || strpos($ci_user_agent, 'msnbot') !== false;
681
		}
682
683
		// We don't know the offset...
684
		$user_info['time_offset'] = 0;
685
	}
686
687
	// Set up the $user_info array.
688
	$user_info += array(
689
		'id' => $id_member,
690
		'username' => $username,
691
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
692
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
693
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
694
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
695
		'is_guest' => $id_member == 0,
696
		'is_admin' => in_array(1, $user_info['groups']),
697
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
698
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
699
		'ip' => $_SERVER['REMOTE_ADDR'],
700
		'ip2' => $_SERVER['BAN_CHECK_IP'],
701
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
702
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
703
		'avatar' => array(
704
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
705
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
706
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
707
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
708
		),
709
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
710
		'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
711
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
712
		'alerts' => empty($user_settings['alerts']) ? 0 : $user_settings['alerts'],
713
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
714
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
715
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
716
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
717
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
718
		'permissions' => array(),
719
	);
720
	$user_info['groups'] = array_unique($user_info['groups']);
721
722
	// Make sure that the last item in the ignore boards array is valid. If the list was too long it could have an ending comma that could cause problems.
723
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
724
		unset($user_info['ignoreboards'][$tmp]);
725
726
	// Allow the user to change their language.
727
	if (!empty($modSettings['userLanguage']))
728
	{
729
		$languages = getLanguages();
730
731
		// Is it valid?
732
		if (!empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
733
		{
734
			$user_info['language'] = strtr($_GET['language'], './\\:', '____');
735
736
			// Make it permanent for members.
737
			if (!empty($user_info['id']))
738
				updateMemberData($user_info['id'], array('lngfile' => $user_info['language']));
739
			else
740
				$_SESSION['language'] = $user_info['language'];
741
		}
742
		elseif (!empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
743
			$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
744
	}
745
746
	// Just build this here, it makes it easier to change/use - administrators can see all boards.
747
	if ($user_info['is_admin'])
748
		$user_info['query_see_board'] = '1=1';
749
	// Otherwise just the groups in $user_info['groups'].
750
	else
751
		$user_info['query_see_board'] = '((FIND_IN_SET(' . implode(', b.member_groups) != 0 OR FIND_IN_SET(', $user_info['groups']) . ', b.member_groups) != 0)' . (!empty($modSettings['deny_boards_access']) ? ' AND (FIND_IN_SET(' . implode(', b.deny_member_groups) = 0 AND FIND_IN_SET(', $user_info['groups']) . ', b.deny_member_groups) = 0)' : '') . (isset($user_info['mod_cache']) ? ' OR ' . $user_info['mod_cache']['mq'] : '') . ')';
752
753
	// Build the list of boards they WANT to see.
754
	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
755
756
	// If they aren't ignoring any boards then they want to see all the boards they can see
757
	if (empty($user_info['ignoreboards']))
758
		$user_info['query_wanna_see_board'] = $user_info['query_see_board'];
759
	// Ok I guess they don't want to see all the boards
760
	else
761
		$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
762
763
	call_integration_hook('integrate_user_info');
764
}
765
766
/**
767
 * Check for moderators and see if they have access to the board.
768
 * What it does:
769
 * - sets up the $board_info array for current board information.
770
 * - if cache is enabled, the $board_info array is stored in cache.
771
 * - redirects to appropriate post if only message id is requested.
772
 * - is only used when inside a topic or board.
773
 * - determines the local moderators for the board.
774
 * - adds group id 3 if the user is a local moderator for the board they are in.
775
 * - prevents access if user is not in proper group nor a local moderator of the board.
776
 */
777
function loadBoard()
778
{
779
	global $txt, $scripturl, $context, $modSettings;
780
	global $board_info, $board, $topic, $user_info, $smcFunc;
781
782
	// Assume they are not a moderator.
783
	$user_info['is_mod'] = false;
784
	$context['user']['is_mod'] = &$user_info['is_mod'];
785
786
	// Start the linktree off empty..
787
	$context['linktree'] = array();
788
789
	// Have they by chance specified a message id but nothing else?
790
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
791
	{
792
		// Make sure the message id is really an int.
793
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
794
795
		// Looking through the message table can be slow, so try using the cache first.
796
		if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === null)
797
		{
798
			$request = $smcFunc['db_query']('', '
799
				SELECT id_topic
800
				FROM {db_prefix}messages
801
				WHERE id_msg = {int:id_msg}
802
				LIMIT 1',
803
				array(
804
					'id_msg' => $_REQUEST['msg'],
805
				)
806
			);
807
808
			// So did it find anything?
809
			if ($smcFunc['db_num_rows']($request))
810
			{
811
				list ($topic) = $smcFunc['db_fetch_row']($request);
812
				$smcFunc['db_free_result']($request);
813
				// Save save save.
814
				cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
815
			}
816
		}
817
818
		// Remember redirection is the key to avoiding fallout from your bosses.
819
		if (!empty($topic))
820
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
821
		else
822
		{
823
			loadPermissions();
824
			loadTheme();
825
			fatal_lang_error('topic_gone', false);
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...
826
		}
827
	}
828
829
	// Load this board only if it is specified.
830
	if (empty($board) && empty($topic))
831
	{
832
		$board_info = array('moderators' => array(), 'moderator_groups' => array());
833
		return;
834
	}
835
836
	if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
837
	{
838
		// @todo SLOW?
839
		if (!empty($topic))
840
			$temp = cache_get_data('topic_board-' . $topic, 120);
841
		else
842
			$temp = cache_get_data('board-' . $board, 120);
843
844
		if (!empty($temp))
845
		{
846
			$board_info = $temp;
847
			$board = $board_info['id'];
848
		}
849
	}
850
851
	if (empty($temp))
852
	{
853
		$request = $smcFunc['db_query']('', '
854
			SELECT
855
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
856
				b.id_parent, c.name AS cname, COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name,
857
				COALESCE(mem.id_member, 0) AS id_moderator,
858
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
859
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
860
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
861
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
862
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
863
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
864
				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
865
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
866
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
867
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
868
			WHERE b.id_board = {raw:board_link}',
869
			array(
870
				'current_topic' => $topic,
871
				'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
872
			)
873
		);
874
		// If there aren't any, skip.
875
		if ($smcFunc['db_num_rows']($request) > 0)
876
		{
877
			$row = $smcFunc['db_fetch_assoc']($request);
878
879
			// Set the current board.
880
			if (!empty($row['id_board']))
881
				$board = $row['id_board'];
882
883
			// Basic operating information. (globals... :/)
884
			$board_info = array(
885
				'id' => $board,
886
				'moderators' => array(),
887
				'moderator_groups' => array(),
888
				'cat' => array(
889
					'id' => $row['id_cat'],
890
					'name' => $row['cname']
891
				),
892
				'name' => $row['bname'],
893
				'description' => $row['description'],
894
				'num_topics' => $row['num_topics'],
895
				'unapproved_topics' => $row['unapproved_topics'],
896
				'unapproved_posts' => $row['unapproved_posts'],
897
				'unapproved_user_topics' => 0,
898
				'parent_boards' => getBoardParents($row['id_parent']),
899
				'parent' => $row['id_parent'],
900
				'child_level' => $row['child_level'],
901
				'theme' => $row['id_theme'],
902
				'override_theme' => !empty($row['override_theme']),
903
				'profile' => $row['id_profile'],
904
				'redirect' => $row['redirect'],
905
				'recycle' => !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board,
906
				'posts_count' => empty($row['count_posts']),
907
				'cur_topic_approved' => empty($topic) || $row['approved'],
908
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
909
			);
910
911
			// Load the membergroups allowed, and check permissions.
912
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
913
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
914
915
			do
916
			{
917 View Code Duplication
				if (!empty($row['id_moderator']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
918
					$board_info['moderators'][$row['id_moderator']] = array(
919
						'id' => $row['id_moderator'],
920
						'name' => $row['real_name'],
921
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
922
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
923
					);
924
925 View Code Duplication
				if (!empty($row['id_moderator_group']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
926
					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
927
						'id' => $row['id_moderator_group'],
928
						'name' => $row['group_name'],
929
						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
930
						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
931
					);
932
			}
933
			while ($row = $smcFunc['db_fetch_assoc']($request));
934
935
			// If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
936
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
937
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
938
			{
939
				// Free the previous result
940
				$smcFunc['db_free_result']($request);
941
942
				// @todo why is this using id_topic?
943
				// @todo Can this get cached?
944
				$request = $smcFunc['db_query']('', '
945
					SELECT COUNT(id_topic)
946
					FROM {db_prefix}topics
947
					WHERE id_member_started={int:id_member}
948
						AND approved = {int:unapproved}
949
						AND id_board = {int:board}',
950
					array(
951
						'id_member' => $user_info['id'],
952
						'unapproved' => 0,
953
						'board' => $board,
954
					)
955
				);
956
957
				list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
958
			}
959
960
			if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
961
			{
962
				// @todo SLOW?
963
				if (!empty($topic))
964
					cache_put_data('topic_board-' . $topic, $board_info, 120);
965
				cache_put_data('board-' . $board, $board_info, 120);
966
			}
967
		}
968
		else
969
		{
970
			// Otherwise the topic is invalid, there are no moderators, etc.
971
			$board_info = array(
972
				'moderators' => array(),
973
				'moderator_groups' => array(),
974
				'error' => 'exist'
975
			);
976
			$topic = null;
977
			$board = 0;
978
		}
979
		$smcFunc['db_free_result']($request);
980
	}
981
982
	if (!empty($topic))
983
		$_GET['board'] = (int) $board;
984
985
	if (!empty($board))
986
	{
987
		// Get this into an array of keys for array_intersect
988
		$moderator_groups = array_keys($board_info['moderator_groups']);
989
990
		// Now check if the user is a moderator.
991
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
992
993 View Code Duplication
		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
994
			$board_info['error'] = 'access';
995 View Code Duplication
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
996
			$board_info['error'] = 'access';
997
998
		// Build up the linktree.
999
		$context['linktree'] = array_merge(
1000
			$context['linktree'],
1001
			array(array(
1002
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
1003
				'name' => $board_info['cat']['name']
1004
			)),
1005
			array_reverse($board_info['parent_boards']),
1006
			array(array(
1007
				'url' => $scripturl . '?board=' . $board . '.0',
1008
				'name' => $board_info['name']
1009
			))
1010
		);
1011
	}
1012
1013
	// Set the template contextual information.
1014
	$context['user']['is_mod'] = &$user_info['is_mod'];
1015
	$context['current_topic'] = $topic;
1016
	$context['current_board'] = $board;
1017
1018
	// No posting in redirection boards!
1019
	if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'post' && !empty($board_info['redirect']))
1020
		$board_info['error'] == 'post_in_redirect';
1021
1022
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
1023
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_mod']))
1024
	{
1025
		// The permissions and theme need loading, just to make sure everything goes smoothly.
1026
		loadPermissions();
1027
		loadTheme();
1028
1029
		$_GET['board'] = '';
1030
		$_GET['topic'] = '';
1031
1032
		// The linktree should not give the game away mate!
1033
		$context['linktree'] = array(
1034
			array(
1035
				'url' => $scripturl,
1036
				'name' => $context['forum_name_html_safe']
1037
			)
1038
		);
1039
1040
		// If it's a prefetching agent or we're requesting an attachment.
1041
		if ((isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') || (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach'))
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $_REQUEST['action'] (integer) and 'dlattach' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
1042
		{
1043
			ob_end_clean();
1044
			header('HTTP/1.1 403 Forbidden');
1045
			die;
1046
		}
1047
		elseif ($board_info['error'] == 'post_in_redirect')
1048
		{
1049
			// Slightly different error message here...
1050
			fatal_lang_error('cannot_post_redirect', false);
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...
1051
		}
1052
		elseif ($user_info['is_guest'])
1053
		{
1054
			loadLanguage('Errors');
1055
			is_not_guest($txt['topic_gone']);
1056
		}
1057
		else
1058
			fatal_lang_error('topic_gone', false);
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...
1059
	}
1060
1061
	if ($user_info['is_mod'])
1062
		$user_info['groups'][] = 3;
1063
}
1064
1065
/**
1066
 * Load this user's permissions.
1067
 */
1068
function loadPermissions()
1069
{
1070
	global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
1071
1072
	if ($user_info['is_admin'])
1073
	{
1074
		banPermissions();
1075
		return;
1076
	}
1077
1078
	if (!empty($modSettings['cache_enable']))
1079
	{
1080
		$cache_groups = $user_info['groups'];
1081
		asort($cache_groups);
1082
		$cache_groups = implode(',', $cache_groups);
1083
		// If it's a spider then cache it different.
1084
		if ($user_info['possibly_robot'])
1085
			$cache_groups .= '-spider';
1086
1087
		if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1088
		{
1089
			list ($user_info['permissions']) = $temp;
1090
			banPermissions();
1091
1092
			return;
1093
		}
1094
		elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1095
			list ($user_info['permissions'], $removals) = $temp;
1096
	}
1097
1098
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
1099
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
1100
1101
	if (empty($user_info['permissions']))
1102
	{
1103
		// Get the general permissions.
1104
		$request = $smcFunc['db_query']('', '
1105
			SELECT permission, add_deny
1106
			FROM {db_prefix}permissions
1107
			WHERE id_group IN ({array_int:member_groups})
1108
				' . $spider_restrict,
1109
			array(
1110
				'member_groups' => $user_info['groups'],
1111
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1112
			)
1113
		);
1114
		$removals = array();
1115 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1116
		{
1117
			if (empty($row['add_deny']))
1118
				$removals[] = $row['permission'];
1119
			else
1120
				$user_info['permissions'][] = $row['permission'];
1121
		}
1122
		$smcFunc['db_free_result']($request);
1123
1124
		if (isset($cache_groups))
1125
			cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
1126
	}
1127
1128
	// Get the board permissions.
1129
	if (!empty($board))
1130
	{
1131
		// Make sure the board (if any) has been loaded by loadBoard().
1132
		if (!isset($board_info['profile']))
1133
			fatal_lang_error('no_board');
1134
1135
		$request = $smcFunc['db_query']('', '
1136
			SELECT permission, add_deny
1137
			FROM {db_prefix}board_permissions
1138
			WHERE (id_group IN ({array_int:member_groups})
1139
				' . $spider_restrict . ')
1140
				AND id_profile = {int:id_profile}',
1141
			array(
1142
				'member_groups' => $user_info['groups'],
1143
				'id_profile' => $board_info['profile'],
1144
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1145
			)
1146
		);
1147 View Code Duplication
		while ($row = $smcFunc['db_fetch_assoc']($request))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1148
		{
1149
			if (empty($row['add_deny']))
1150
				$removals[] = $row['permission'];
0 ignored issues
show
Bug introduced by
The variable $removals 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...
1151
			else
1152
				$user_info['permissions'][] = $row['permission'];
1153
		}
1154
		$smcFunc['db_free_result']($request);
1155
	}
1156
1157
	// Remove all the permissions they shouldn't have ;).
1158
	if (!empty($modSettings['permission_enable_deny']))
1159
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
1160
1161
	if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
1162
		cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
1163
1164
	// Banned?  Watch, don't touch..
1165
	banPermissions();
1166
1167
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
1168
	if (!$user_info['is_guest'])
1169
	{
1170
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
1171
		{
1172
			require_once($sourcedir . '/Subs-Auth.php');
1173
			rebuildModCache();
1174
		}
1175
		else
1176
			$user_info['mod_cache'] = $_SESSION['mc'];
1177
1178
		// This is a useful phantom permission added to the current user, and only the current user while they are logged in.
1179
		// For example this drastically simplifies certain changes to the profile area.
1180
		$user_info['permissions'][] = 'is_not_guest';
1181
		// And now some backwards compatibility stuff for mods and whatnot that aren't expecting the new permissions.
1182
		$user_info['permissions'][] = 'profile_view_own';
1183
		if (in_array('profile_view', $user_info['permissions']))
1184
			$user_info['permissions'][] = 'profile_view_any';
1185
	}
1186
}
1187
1188
/**
1189
 * Loads an array of users' data by ID or member_name.
1190
 *
1191
 * @param array|string $users An array of users by id or name or a single username/id
1192
 * @param bool $is_name Whether $users contains names
1193
 * @param string $set What kind of data to load (normal, profile, minimal)
1194
 * @return array The ids of the members loaded
1195
 */
1196
function loadMemberData($users, $is_name = false, $set = 'normal')
1197
{
1198
	global $user_profile, $modSettings, $board_info, $smcFunc, $context;
1199
	global $image_proxy_enabled, $image_proxy_secret, $boardurl;
1200
1201
	// Can't just look for no users :P.
1202
	if (empty($users))
1203
		return array();
1204
1205
	// Pass the set value
1206
	$context['loadMemberContext_set'] = $set;
1207
1208
	// Make sure it's an array.
1209
	$users = !is_array($users) ? array($users) : array_unique($users);
1210
	$loaded_ids = array();
1211
1212
	if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1213
	{
1214
		$users = array_values($users);
1215
		for ($i = 0, $n = count($users); $i < $n; $i++)
1216
		{
1217
			$data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
1218
			if ($data == null)
1219
				continue;
1220
1221
			$loaded_ids[] = $data['id_member'];
1222
			$user_profile[$data['id_member']] = $data;
1223
			unset($users[$i]);
1224
		}
1225
	}
1226
1227
	// Used by default
1228
	$select_columns = '
1229
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
1230
			mem.signature, mem.personal_text, mem.avatar, mem.id_member, mem.member_name,
1231
			mem.real_name, mem.email_address, mem.date_registered, mem.website_title, mem.website_url,
1232
			mem.birthdate, mem.member_ip, mem.member_ip2, mem.posts, mem.last_login, mem.id_post_group, mem.lngfile, mem.id_group, mem.time_offset, mem.show_online,
1233
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
1234
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
1235
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
1236
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
1237
	$select_tables = '
1238
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
1239
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
1240
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1241
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
1242
1243
	// We add or replace according the the set
1244
	switch ($set)
1245
	{
1246
		case 'normal':
1247
			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
1248
			break;
1249
		case 'profile':
1250
			$select_columns .= ', mem.additional_groups, mem.id_theme, mem.pm_ignore_list, mem.pm_receive_from,
1251
			mem.time_format, mem.timezone, mem.secret_question, mem.smiley_set, mem.tfa_secret,
1252
			mem.total_time_logged_in, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.alerts';
1253
			break;
1254
		case 'minimal':
1255
			$select_columns = '
1256
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.date_registered,
1257
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
1258
			$select_tables = '';
1259
			break;
1260
		default:
1261
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
1262
	}
1263
1264
	// Allow mods to easily add to the selected member data
1265
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, &$set));
1266
1267
	if (!empty($users))
1268
	{
1269
		// Load the member's data.
1270
		$request = $smcFunc['db_query']('', '
1271
			SELECT' . $select_columns . '
1272
			FROM {db_prefix}members AS mem' . $select_tables . '
1273
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})',
1274
			array(
1275
				'blank_string' => '',
1276
				'users' => $users,
1277
			)
1278
		);
1279
		$new_loaded_ids = array();
1280
		while ($row = $smcFunc['db_fetch_assoc']($request))
1281
		{
1282
			// Take care of proxying avatar if required, do this here for maximum reach
1283 View Code Duplication
			if ($image_proxy_enabled && !empty($row['avatar']) && stripos($row['avatar'], 'http://') !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1284
				$row['avatar'] = $boardurl . '/proxy.php?request=' . urlencode($row['avatar']) . '&hash=' . md5($row['avatar'] . $image_proxy_secret);
1285
1286
			if ( isset($row['member_ip']) )
1287
				$row['member_ip'] = inet_dtop($row['member_ip']);
1288
			if ( isset($row['member_ip2']) )
1289
				$row['member_ip2'] = inet_dtop($row['member_ip2']);
1290
			$new_loaded_ids[] = $row['id_member'];
1291
			$loaded_ids[] = $row['id_member'];
1292
			$row['options'] = array();
1293
			$user_profile[$row['id_member']] = $row;
1294
		}
1295
		$smcFunc['db_free_result']($request);
1296
	}
1297
1298 View Code Duplication
	if (!empty($new_loaded_ids) && $set !== 'minimal')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1299
	{
1300
		$request = $smcFunc['db_query']('', '
1301
			SELECT id_member, variable, value
1302
			FROM {db_prefix}themes
1303
			WHERE id_member IN ({array_int:loaded_ids})',
1304
			array(
1305
				'loaded_ids' => $new_loaded_ids,
1306
			)
1307
		);
1308
		while ($row = $smcFunc['db_fetch_assoc']($request))
1309
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1310
		$smcFunc['db_free_result']($request);
1311
	}
1312
1313
	$additional_mods = array();
1314
1315
	// Are any of these users in groups assigned to moderate this board?
1316
	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
1317
	{
1318
		foreach ($loaded_ids as $a_member)
1319
		{
1320
			if (!empty($user_profile[$a_member]['additional_groups']))
1321
				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
1322
			else
1323
				$groups = array($user_profile[$a_member]['id_group']);
1324
1325
			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
1326
1327
			if (!empty($temp))
1328
			{
1329
				$additional_mods[] = $a_member;
1330
			}
1331
		}
1332
	}
1333
1334
	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1335
	{
1336
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1337
			cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1338
	}
1339
1340
	// Are we loading any moderators?  If so, fix their group data...
1341
	if (!empty($loaded_ids) && (!empty($board_info['moderators']) || !empty($board_info['moderator_groups'])) && $set === 'normal' && count($temp_mods = array_merge(array_intersect($loaded_ids, array_keys($board_info['moderators'])), $additional_mods)) !== 0)
1342
	{
1343 View Code Duplication
		if (($row = cache_get_data('moderator_group_info', 480)) == null)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1344
		{
1345
			$request = $smcFunc['db_query']('', '
1346
				SELECT group_name AS member_group, online_color AS member_group_color, icons
1347
				FROM {db_prefix}membergroups
1348
				WHERE id_group = {int:moderator_group}
1349
				LIMIT 1',
1350
				array(
1351
					'moderator_group' => 3,
1352
				)
1353
			);
1354
			$row = $smcFunc['db_fetch_assoc']($request);
1355
			$smcFunc['db_free_result']($request);
1356
1357
			cache_put_data('moderator_group_info', $row, 480);
1358
		}
1359
1360
		foreach ($temp_mods as $id)
1361
		{
1362
			// By popular demand, don't show admins or global moderators as moderators.
1363
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1364
				$user_profile[$id]['member_group'] = $row['member_group'];
1365
1366
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1367
			if (!empty($row['icons']))
1368
				$user_profile[$id]['icons'] = $row['icons'];
1369
			if (!empty($row['member_group_color']))
1370
				$user_profile[$id]['member_group_color'] = $row['member_group_color'];
1371
		}
1372
	}
1373
1374
	return $loaded_ids;
1375
}
1376
1377
/**
1378
 * Loads the user's basic values... meant for template/theme usage.
1379
 *
1380
 * @param int $user The ID of a user previously loaded by {@link loadMemberData()}
1381
 * @param bool $display_custom_fields Whether or not to display custom profile fields
1382
 * @return boolean Whether or not the data was loaded successfully
1383
 */
1384
function loadMemberContext($user, $display_custom_fields = false)
1385
{
1386
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1387
	global $context, $modSettings, $settings, $smcFunc;
1388
	static $dataLoaded = array();
1389
	static $loadedLanguages = array();
1390
1391
	// If this person's data is already loaded, skip it.
1392
	if (isset($dataLoaded[$user]))
1393
		return true;
1394
1395
	// We can't load guests or members not loaded by loadMemberData()!
1396
	if ($user == 0)
1397
		return false;
1398
	if (!isset($user_profile[$user]))
1399
	{
1400
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1401
		return false;
1402
	}
1403
1404
	// Well, it's loaded now anyhow.
1405
	$dataLoaded[$user] = true;
1406
	$profile = $user_profile[$user];
1407
1408
	// Censor everything.
1409
	censorText($profile['signature']);
1410
	censorText($profile['personal_text']);
1411
1412
	// Set things up to be used before hand.
1413
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br>', ''), $profile['signature']);
1414
	$profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1415
1416
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1417
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1418
	// Setup the buddy status here (One whole in_array call saved :P)
1419
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1420
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1421
1422
	//We need a little fallback for the membergroup icons. If it doesn't exist in the current theme, fallback to default theme
1423
	if (isset($profile['icons'][1]) && file_exists($settings['actual_theme_dir'] . '/images/membericons/' . $profile['icons'][1])) //icon is set and exists
1424
		$group_icon_url = $settings['images_url'] . '/membericons/' . $profile['icons'][1];
1425
	elseif (isset($profile['icons'][1])) //icon is set and doesn't exist, fallback to default
1426
		$group_icon_url = $settings['default_images_url'] . '/membericons/' . $profile['icons'][1];
1427
	else //not set, bye bye
1428
		$group_icon_url = '';
1429
1430
	// These minimal values are always loaded
1431
	$memberContext[$user] = array(
1432
		'username' => $profile['member_name'],
1433
		'name' => $profile['real_name'],
1434
		'id' => $profile['id_member'],
1435
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1436
		'link' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '" '. (!empty($modSettings['onlineEnable']) ? 'class="pm_icon"' : '').'>' . $profile['real_name'] . '</a>',
1437
		'email' => $profile['email_address'],
1438
		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
1439
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1440
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1441
	);
1442
1443
	// If the set isn't minimal then load the monstrous array.
1444
	if ($context['loadMemberContext_set'] != 'minimal')
1445
	{
1446
		// Go the extra mile and load the user's native language name.
1447
		if (empty($loadedLanguages))
1448
			$loadedLanguages = getLanguages();
1449
1450
		$memberContext[$user] += array(
1451
			'username_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['member_name'] .'</span>',
1452
			'name_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['real_name'] .'</span>',
1453
			'link_color' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . $profile['real_name'] . '" '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>' . $profile['real_name'] . '</a>',
1454
			'is_buddy' => $profile['buddy'],
1455
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1456
			'buddies' => $buddy_list,
1457
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1458
			'blurb' => $profile['personal_text'],
1459
			'website' => array(
1460
				'title' => $profile['website_title'],
1461
				'url' => $profile['website_url'],
1462
			),
1463
			'birth_date' => empty($profile['birthdate']) || $profile['birthdate'] === '0001-01-01' ? '0000-00-00' : (substr($profile['birthdate'], 0, 4) === '0004' ? '0000' . substr($profile['birthdate'], 4) : $profile['birthdate']),
1464
			'signature' => $profile['signature'],
1465
			'real_posts' => $profile['posts'],
1466
			'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1467
			'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1468
			'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(0, $profile['last_login']),
0 ignored issues
show
Documentation introduced by
0 is of type integer, but the function expects a boolean.

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...
1469
			'ip' => $smcFunc['htmlspecialchars']($profile['member_ip']),
1470
			'ip2' => $smcFunc['htmlspecialchars']($profile['member_ip2']),
1471
			'online' => array(
1472
				'is_online' => $profile['is_online'],
1473
				'text' => $smcFunc['htmlspecialchars']($txt[$profile['is_online'] ? 'online' : 'offline']),
1474
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], $smcFunc['htmlspecialchars']($profile['real_name'])),
1475
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1476
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1477
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1478
			),
1479
			'language' => !empty($loadedLanguages[$profile['lngfile']]) && !empty($loadedLanguages[$profile['lngfile']]['name']) ? $loadedLanguages[$profile['lngfile']]['name'] : $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1480
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1481
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1482
			'options' => $profile['options'],
1483
			'is_guest' => false,
1484
			'group' => $profile['member_group'],
1485
			'group_color' => $profile['member_group_color'],
1486
			'group_id' => $profile['id_group'],
1487
			'post_group' => $profile['post_group'],
1488
			'post_group_color' => $profile['post_group_color'],
1489
			'group_icons' => str_repeat('<img src="' . str_replace('$language', $context['user']['language'], isset($profile['icons'][1]) ? $group_icon_url : '') . '" alt="*">', empty($profile['icons'][0]) || empty($profile['icons'][1]) ? 0 : $profile['icons'][0]),
1490
			'warning' => $profile['warning'],
1491
			'warning_status' => !empty($modSettings['warning_mute']) && $modSettings['warning_mute'] <= $profile['warning'] ? 'mute' : (!empty($modSettings['warning_moderate']) && $modSettings['warning_moderate'] <= $profile['warning'] ? 'moderate' : (!empty($modSettings['warning_watch']) && $modSettings['warning_watch'] <= $profile['warning'] ? 'watch' : (''))),
1492
			'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1493
			'custom_fields' => array(),
1494
		);
1495
	}
1496
1497
	// If the set isn't minimal then load their avatar as well.
1498
	if ($context['loadMemberContext_set'] != 'minimal')
1499
	{
1500
		if (!empty($modSettings['gravatarOverride']) || (!empty($modSettings['gravatarEnabled']) && stristr($profile['avatar'], 'gravatar://')))
1501
		{
1502
			if (!empty($modSettings['gravatarAllowExtraEmail']) && stristr($profile['avatar'], 'gravatar://') && strlen($profile['avatar']) > 11)
1503
				$image = get_gravatar_url($smcFunc['substr']($profile['avatar'], 11));
1504
			else
1505
				$image = get_gravatar_url($profile['email_address']);
1506
		}
1507
		else
1508
		{
1509
			// So it's stored in the member table?
1510
			if (!empty($profile['avatar']))
1511
			{
1512
				$image = (stristr($profile['avatar'], 'http://') || stristr($profile['avatar'], 'https://')) ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'];
1513
			}
1514
			elseif (!empty($profile['filename']))
1515
				$image = $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1516
			// Right... no avatar...use the default one
1517
			else
1518
				$image = $modSettings['avatar_url'] . '/default.png';
1519
		}
1520
		if (!empty($image))
1521
			$memberContext[$user]['avatar'] = array(
1522
				'name' => $profile['avatar'],
1523
				'image' => '<img class="avatar" src="' . $image . '" alt="avatar_'. $profile['member_name'].'">',
1524
				'href' => $image,
1525
				'url' => $image,
1526
			);
1527
	}
1528
1529
	// Are we also loading the members custom fields into context?
1530
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1531
	{
1532
		$memberContext[$user]['custom_fields'] = array();
1533
1534
		if (!isset($context['display_fields']))
1535
			$context['display_fields'] = smf_json_decode($modSettings['displayFields'], true);
1536
1537
		foreach ($context['display_fields'] as $custom)
1538
		{
1539
			if (!isset($custom['col_name']) || trim($custom['col_name']) == '' || empty($profile['options'][$custom['col_name']]))
1540
				continue;
1541
1542
			$value = $profile['options'][$custom['col_name']];
1543
1544
			// Don't show the "disabled" option for the "gender" field.
1545
			if ($custom['col_name'] == 'cust_gender' && $value == 'Disabled')
1546
				continue;
1547
1548
			// BBC?
1549
			if ($custom['bbc'])
1550
				$value = parse_bbc($value);
1551
			// ... or checkbox?
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1552
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1553
				$value = $value ? $txt['yes'] : $txt['no'];
1554
1555
			// Enclosing the user input within some other text?
1556 View Code Duplication
			if (!empty($custom['enclose']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1557
				$value = strtr($custom['enclose'], array(
1558
					'{SCRIPTURL}' => $scripturl,
1559
					'{IMAGES_URL}' => $settings['images_url'],
1560
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1561
					'{INPUT}' => $value,
1562
				));
1563
1564
			$memberContext[$user]['custom_fields'][] = array(
1565
				'title' => !empty($custom['title']) ? $custom['title'] : $custom['col_name'],
1566
				'col_name' => $custom['col_name'],
1567
				'value' => un_htmlspecialchars($value),
1568
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1569
			);
1570
		}
1571
	}
1572
1573
	call_integration_hook('integrate_member_context', array(&$memberContext[$user], $user, $display_custom_fields));
1574
	return true;
1575
}
1576
1577
/**
1578
 * Loads the user's custom profile fields
1579
 *
1580
 * @param integer|array $users A single user ID or an array of user IDs
1581
 * @param string|array $params Either a string or an array of strings with profile field names
1582
 * @return array|boolean An array of data about the fields and their values or false if nothing was loaded
1583
 */
1584
function loadMemberCustomFields($users, $params)
1585
{
1586
	global $smcFunc, $txt, $scripturl, $settings;
1587
1588
	// Do not waste my time...
1589
	if (empty($users) || empty($params))
1590
		return false;
1591
1592
	// Make sure it's an array.
1593
	$users = !is_array($users) ? array($users) : array_unique($users);
1594
	$params = !is_array($params) ? array($params) : array_unique($params);
1595
	$return = array();
1596
1597
	$request = $smcFunc['db_query']('', '
1598
		SELECT c.id_field, c.col_name, c.field_name, c.field_desc, c.field_type, c.field_order, c.field_length, c.field_options, c.mask, show_reg,
1599
		c.show_display, c.show_profile, c.private, c.active, c.bbc, c.can_search, c.default_value, c.enclose, c.placement, t.variable, t.value, t.id_member
1600
		FROM {db_prefix}themes AS t
1601
			LEFT JOIN {db_prefix}custom_fields AS c ON (c.col_name = t.variable)
1602
		WHERE id_member IN ({array_int:loaded_ids})
1603
			AND variable IN ({array_string:params})
1604
		ORDER BY field_order',
1605
		array(
1606
			'loaded_ids' => $users,
1607
			'params' => $params,
1608
		)
1609
	);
1610
1611
	while ($row = $smcFunc['db_fetch_assoc']($request))
1612
	{
1613
		// BBC?
1614
		if (!empty($row['bbc']))
1615
			$row['value'] = parse_bbc($row['value']);
1616
1617
		// ... or checkbox?
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1618
		elseif (isset($row['type']) && $row['type'] == 'check')
1619
			$row['value'] = !empty($row['value']) ? $txt['yes'] : $txt['no'];
1620
1621
		// Enclosing the user input within some other text?
1622
		if (!empty($row['enclose']))
1623
			$row['value'] = strtr($row['enclose'], array(
1624
				'{SCRIPTURL}' => $scripturl,
1625
				'{IMAGES_URL}' => $settings['images_url'],
1626
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1627
				'{INPUT}' => un_htmlspecialchars($row['value']),
1628
			));
1629
1630
		// Send a simple array if there is just 1 param
1631
		if (count($params) == 1)
1632
			$return[$row['id_member']] = $row;
1633
1634
		// More than 1? knock yourself out...
1635
		else
1636
		{
1637
			if (!isset($return[$row['id_member']]))
1638
				$return[$row['id_member']] = array();
1639
1640
			$return[$row['id_member']][$row['variable']] = $row;
1641
		}
1642
	}
1643
1644
	$smcFunc['db_free_result']($request);
1645
1646
	return !empty($return) ? $return : false;
1647
}
1648
1649
/**
1650
 * Loads information about what browser the user is viewing with and places it in $context
1651
 *  - uses the class from {@link Class-BrowserDetect.php}
1652
 */
1653
function detectBrowser()
1654
{
1655
	// Load the current user's browser of choice
1656
	$detector = new browser_detector;
1657
	$detector->detectBrowser();
1658
}
1659
1660
/**
1661
 * Are we using this browser?
1662
 *
1663
 * Wrapper function for detectBrowser
1664
 * @param string $browser The browser we are checking for.
1665
 * @return bool Whether or not the current browser is what we're looking for
1666
*/
1667
function isBrowser($browser)
1668
{
1669
	global $context;
1670
1671
	// Don't know any browser!
1672
	if (empty($context['browser']))
1673
		detectBrowser();
1674
1675
	return !empty($context['browser'][$browser]) || !empty($context['browser']['is_' . $browser]) ? true : false;
1676
}
1677
1678
/**
1679
 * Load a theme, by ID.
1680
 *
1681
 * @param int $id_theme The ID of the theme to load
1682
 * @param bool $initialize Whether or not to initialize a bunch of theme-related variables/settings
1683
 */
1684
function loadTheme($id_theme = 0, $initialize = true)
1685
{
1686
	global $user_info, $user_settings, $board_info, $boarddir, $maintenance;
1687
	global $txt, $boardurl, $scripturl, $mbname, $modSettings;
1688
	global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc, $language, $board, $image_proxy_enabled;
1689
1690
	// The theme was specified by parameter.
1691
	if (!empty($id_theme))
1692
		$id_theme = (int) $id_theme;
1693
	// The theme was specified by REQUEST.
1694 View Code Duplication
	elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1695
	{
1696
		$id_theme = (int) $_REQUEST['theme'];
1697
		$_SESSION['id_theme'] = $id_theme;
1698
	}
1699
	// The theme was specified by REQUEST... previously.
1700 View Code Duplication
	elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1701
		$id_theme = (int) $_SESSION['id_theme'];
1702
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1703
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']))
1704
		$id_theme = $user_info['theme'];
1705
	// The theme was specified by the board.
1706
	elseif (!empty($board_info['theme']))
1707
		$id_theme = $board_info['theme'];
1708
	// The theme is the forum's default.
1709
	else
1710
		$id_theme = $modSettings['theme_guests'];
1711
1712
	// Verify the id_theme... no foul play.
1713
	// Always allow the board specific theme, if they are overriding.
1714
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1715
		$id_theme = $board_info['theme'];
1716
	// If they have specified a particular theme to use with SSI allow it to be used.
1717
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1718
		$id_theme = (int) $id_theme;
1719
	elseif (!empty($modSettings['enableThemes']) && !allowedTo('admin_forum'))
1720
	{
1721
		$themes = explode(',', $modSettings['enableThemes']);
1722
		if (!in_array($id_theme, $themes))
1723
			$id_theme = $modSettings['theme_guests'];
1724
		else
1725
			$id_theme = (int) $id_theme;
1726
	}
1727
	else
1728
		$id_theme = (int) $id_theme;
1729
1730
	$member = empty($user_info['id']) ? -1 : $user_info['id'];
1731
1732
	// Disable image proxy if we don't have SSL enabled
1733
	if (empty($modSettings['force_ssl']) || $modSettings['force_ssl'] < 2)
1734
		$image_proxy_enabled = false;
1735
1736
	if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2 && ($temp = cache_get_data('theme_settings-' . $id_theme . ':' . $member, 60)) != null && time() - 60 > $modSettings['settings_updated'])
1737
	{
1738
		$themeData = $temp;
1739
		$flag = true;
1740
	}
1741
	elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1742
		$themeData = $temp + array($member => array());
1743
	else
1744
		$themeData = array(-1 => array(), 0 => array(), $member => array());
1745
1746
	if (empty($flag))
1747
	{
1748
		// Load variables from the current or default theme, global or this user's.
1749
		$result = $smcFunc['db_query']('', '
1750
			SELECT variable, value, id_member, id_theme
1751
			FROM {db_prefix}themes
1752
			WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1753
				AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1754
			array(
1755
				'id_theme' => $id_theme,
1756
				'id_member' => $member,
1757
			)
1758
		);
1759
		// Pick between $settings and $options depending on whose data it is.
1760
		while ($row = $smcFunc['db_fetch_assoc']($result))
1761
		{
1762
			// There are just things we shouldn't be able to change as members.
1763
			if ($row['id_member'] != 0 && in_array($row['variable'], array('actual_theme_url', 'actual_images_url', 'base_theme_dir', 'base_theme_url', 'default_images_url', 'default_theme_dir', 'default_theme_url', 'default_template', 'images_url', 'number_recent_posts', 'smiley_sets_default', 'theme_dir', 'theme_id', 'theme_layers', 'theme_templates', 'theme_url')))
1764
				continue;
1765
1766
			// If this is the theme_dir of the default theme, store it.
1767
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1768
				$themeData[0]['default_' . $row['variable']] = $row['value'];
1769
1770
			// If this isn't set yet, is a theme option, or is not the default theme..
1771
			if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1772
				$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1773
		}
1774
		$smcFunc['db_free_result']($result);
1775
1776
		if (!empty($themeData[-1]))
1777
			foreach ($themeData[-1] as $k => $v)
1778
			{
1779
				if (!isset($themeData[$member][$k]))
1780
					$themeData[$member][$k] = $v;
1781
			}
1782
1783
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1784
			cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1785
		// Only if we didn't already load that part of the cache...
1786
		elseif (!isset($temp))
1787
			cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1788
	}
1789
1790
	$settings = $themeData[0];
1791
	$options = $themeData[$member];
1792
1793
	$settings['theme_id'] = $id_theme;
1794
1795
	$settings['actual_theme_url'] = $settings['theme_url'];
1796
	$settings['actual_images_url'] = $settings['images_url'];
1797
	$settings['actual_theme_dir'] = $settings['theme_dir'];
1798
1799
	$settings['template_dirs'] = array();
1800
	// This theme first.
1801
	$settings['template_dirs'][] = $settings['theme_dir'];
1802
1803
	// Based on theme (if there is one).
1804
	if (!empty($settings['base_theme_dir']))
1805
		$settings['template_dirs'][] = $settings['base_theme_dir'];
1806
1807
	// Lastly the default theme.
1808 View Code Duplication
	if ($settings['theme_dir'] != $settings['default_theme_dir'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1809
		$settings['template_dirs'][] = $settings['default_theme_dir'];
1810
1811
	if (!$initialize)
1812
		return;
1813
1814
	// Check to see if we're forcing SSL
1815
	if (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] == 2 && empty($maintenance) &&
1816
		(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') && SMF != 'SSI')
1817
		redirectexit(strtr($_SERVER['REQUEST_URL'], array('http://' => 'https://')));
1818
1819
	// Check to see if they're accessing it from the wrong place.
1820
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1821
	{
1822
		$detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1823
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1824
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1825
		if ($temp != '/')
1826
			$detected_url .= $temp;
1827
	}
1828
	if (isset($detected_url) && $detected_url != $boardurl)
1829
	{
1830
		// Try #1 - check if it's in a list of alias addresses.
1831
		if (!empty($modSettings['forum_alias_urls']))
1832
		{
1833
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1834
1835
			foreach ($aliases as $alias)
1836
			{
1837
				// Rip off all the boring parts, spaces, etc.
1838
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1839
					$do_fix = true;
1840
			}
1841
		}
1842
1843
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1844
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
1845
		{
1846
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1847
			if (empty($_GET))
1848
				redirectexit('wwwRedirect');
1849
			else
1850
			{
1851
				list ($k, $v) = each($_GET);
1852
1853
				if ($k != 'wwwRedirect')
1854
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1855
			}
1856
		}
1857
1858
		// #3 is just a check for SSL...
1859
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1860
			$do_fix = true;
1861
1862
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1863
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1864
		{
1865
			// Caching is good ;).
1866
			$oldurl = $boardurl;
1867
1868
			// Fix $boardurl and $scripturl.
1869
			$boardurl = $detected_url;
1870
			$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1871
			$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1872
1873
			// Fix the theme urls...
1874
			$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1875
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1876
			$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1877
			$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1878
			$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1879
			$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1880
1881
			// And just a few mod settings :).
1882
			$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1883
			$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1884
1885
			// Clean up after loadBoard().
1886
			if (isset($board_info['moderators']))
1887
			{
1888
				foreach ($board_info['moderators'] as $k => $dummy)
1889
				{
1890
					$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1891
					$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1892
				}
1893
			}
1894
			foreach ($context['linktree'] as $k => $dummy)
1895
				$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1896
		}
1897
	}
1898
	// Set up the contextual user array.
1899
	if (!empty($user_info))
1900
	{
1901
		$context['user'] = array(
1902
			'id' => $user_info['id'],
1903
			'is_logged' => !$user_info['is_guest'],
1904
			'is_guest' => &$user_info['is_guest'],
1905
			'is_admin' => &$user_info['is_admin'],
1906
			'is_mod' => &$user_info['is_mod'],
1907
			// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1908
			'can_mod' => allowedTo('access_mod_center') || (!$user_info['is_guest'] && ($user_info['mod_cache']['gq'] != '0=1' || $user_info['mod_cache']['bq'] != '0=1' || ($modSettings['postmod_active'] && !empty($user_info['mod_cache']['ap'])))),
1909
			'username' => $user_info['username'],
1910
			'language' => $user_info['language'],
1911
			'email' => $user_info['email'],
1912
			'ignoreusers' => $user_info['ignoreusers'],
1913
		);
1914
		if (!$context['user']['is_guest'])
1915
			$context['user']['name'] = $user_info['name'];
1916 View Code Duplication
		elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1917
			$context['user']['name'] = $txt['guest_title'];
1918
1919
		// Determine the current smiley set.
1920
		$user_info['smiley_set'] = (!in_array($user_info['smiley_set'], explode(',', $modSettings['smiley_sets_known'])) && $user_info['smiley_set'] != 'none') || empty($modSettings['smiley_sets_enable']) ? (!empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default']) : $user_info['smiley_set'];
1921
		$context['user']['smiley_set'] = $user_info['smiley_set'];
1922
	}
1923
	else
1924
	{
1925
		$context['user'] = array(
1926
			'id' => -1,
1927
			'is_logged' => false,
1928
			'is_guest' => true,
1929
			'is_mod' => false,
1930
			'can_mod' => false,
1931
			'name' => $txt['guest_title'],
1932
			'language' => $language,
1933
			'email' => '',
1934
			'ignoreusers' => array(),
1935
		);
1936
	}
1937
1938
	// Some basic information...
1939
	if (!isset($context['html_headers']))
1940
		$context['html_headers'] = '';
1941
	if (!isset($context['javascript_files']))
1942
		$context['javascript_files'] = array();
1943
	if (!isset($context['css_files']))
1944
		$context['css_files'] = array();
1945
	if (!isset($context['css_header']))
1946
		$context['css_header'] = array();
1947
	if (!isset($context['javascript_inline']))
1948
		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
1949
	if (!isset($context['javascript_vars']))
1950
		$context['javascript_vars'] = array();
1951
1952
	$context['login_url'] = (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] < 2 ? strtr($scripturl, array('http://' => 'https://')) : $scripturl) . '?action=login2';
1953
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1954
	$context['session_var'] = $_SESSION['session_var'];
1955
	$context['session_id'] = $_SESSION['session_value'];
1956
	$context['forum_name'] = $mbname;
1957
	$context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
1958
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
1959
	$context['current_action'] = isset($_REQUEST['action']) ? $smcFunc['htmlspecialchars']($_REQUEST['action']) : null;
1960
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1961
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
1962
	if (isset($modSettings['load_average']))
1963
		$context['load_average'] = $modSettings['load_average'];
1964
1965
	// Detect the browser. This is separated out because it's also used in attachment downloads
1966
	detectBrowser();
1967
1968
	// Set the top level linktree up.
1969
	array_unshift($context['linktree'], array(
1970
		'url' => $scripturl,
1971
		'name' => $context['forum_name_html_safe']
1972
	));
1973
1974
	// This allows sticking some HTML on the page output - useful for controls.
1975
	$context['insert_after_template'] = '';
1976
1977
	if (!isset($txt))
1978
		$txt = array();
1979
1980
	$simpleActions = array(
1981
		'findmember',
1982
		'helpadmin',
1983
		'printpage',
1984
		'spellcheck',
1985
	);
1986
1987
	// Parent action => array of areas
1988
	$simpleAreas = array(
1989
		'profile' => array('popup', 'alerts_popup',),
1990
	);
1991
1992
	// Parent action => array of subactions
1993
	$simpleSubActions = array(
1994
		'pm' => array('popup',),
1995
		'signup' => array('usernamecheck'),
1996
	);
1997
1998
	// Extra params like ;preview ;js, etc.
1999
	$extraParams = array(
2000
		'preview',
2001
		'splitjs',
2002
	);
2003
2004
	// Actions that specifically uses XML output.
2005
	$xmlActions = array(
2006
		'quotefast',
2007
		'jsmodify',
2008
		'xmlhttp',
2009
		'post2',
2010
		'suggest',
2011
		'stats',
2012
		'notifytopic',
2013
		'notifyboard',
2014
	);
2015
2016
	call_integration_hook('integrate_simple_actions', array(&$simpleActions, &$simpleAreas, &$simpleSubActions, &$extraParams, &$xmlActions));
2017
2018
	$context['simple_action'] = in_array($context['current_action'], $simpleActions) ||
2019
	(isset($simpleAreas[$context['current_action']]) && isset($_REQUEST['area']) && in_array($_REQUEST['area'], $simpleAreas[$context['current_action']])) ||
2020
	(isset($simpleSubActions[$context['current_action']]) && in_array($context['current_subaction'], $simpleSubActions[$context['current_action']]));
2021
2022
	// See if theres any extra param to check.
2023
	$requiresXML = false;
2024
	foreach ($extraParams as $key => $extra)
2025
		if (isset($_REQUEST[$extra]))
2026
			$requiresXML = true;
2027
2028
	// Output is fully XML, so no need for the index template.
2029
	if (isset($_REQUEST['xml']) && (in_array($context['current_action'], $xmlActions) || $requiresXML))
2030
	{
2031
		loadLanguage('index+Modifications');
2032
		loadTemplate('Xml');
2033
		$context['template_layers'] = array();
2034
	}
2035
2036
	// These actions don't require the index template at all.
2037
	elseif (!empty($context['simple_action']))
2038
	{
2039
		loadLanguage('index+Modifications');
2040
		$context['template_layers'] = array();
2041
	}
2042
2043
	else
2044
	{
2045
		// Custom templates to load, or just default?
2046
		if (isset($settings['theme_templates']))
2047
			$templates = explode(',', $settings['theme_templates']);
2048
		else
2049
			$templates = array('index');
2050
2051
		// Load each template...
2052
		foreach ($templates as $template)
2053
			loadTemplate($template);
2054
2055
		// ...and attempt to load their associated language files.
2056
		$required_files = implode('+', array_merge($templates, array('Modifications')));
2057
		loadLanguage($required_files, '', false);
2058
2059
		// Custom template layers?
2060
		if (isset($settings['theme_layers']))
2061
			$context['template_layers'] = explode(',', $settings['theme_layers']);
2062
		else
2063
			$context['template_layers'] = array('html', 'body');
2064
	}
2065
2066
	// Initialize the theme.
2067
	loadSubTemplate('init', 'ignore');
0 ignored issues
show
Documentation introduced by
'ignore' is of type string, but the function expects a boolean.

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...
2068
2069
	// Allow overriding the board wide time/number formats.
2070
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
2071
		$user_info['time_format'] = $txt['time_format'];
2072
2073
	// Set the character set from the template.
2074
	$context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
2075
	$context['utf8'] = $context['character_set'] === 'UTF-8';
2076
	$context['right_to_left'] = !empty($txt['lang_rtl']);
2077
2078
	// Guests may still need a name.
2079 View Code Duplication
	if ($context['user']['is_guest'] && empty($context['user']['name']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2080
		$context['user']['name'] = $txt['guest_title'];
2081
2082
	// Any theme-related strings that need to be loaded?
2083
	if (!empty($settings['require_theme_strings']))
2084
		loadLanguage('ThemeStrings', '', false);
2085
2086
	// Make a special URL for the language.
2087
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
2088
2089
	// And of course, let's load the default CSS file.
2090
	loadCSSFile('index.css', array('minimize' => true), 'smf_index');
2091
2092
	// Here is my luvly Responsive CSS
2093
	loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true), 'smf_responsive');
2094
2095
	if ($context['right_to_left'])
2096
		loadCSSFile('rtl.css', array(), 'smf_rtl');
2097
2098
	// We allow theme variants, because we're cool.
2099
	$context['theme_variant'] = '';
2100
	$context['theme_variant_url'] = '';
2101
	if (!empty($settings['theme_variants']))
2102
	{
2103
		// Overriding - for previews and that ilk.
2104
		if (!empty($_REQUEST['variant']))
2105
			$_SESSION['id_variant'] = $_REQUEST['variant'];
2106
		// User selection?
2107
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
2108
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
2109
		// If not a user variant, select the default.
2110
		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
2111
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
2112
2113
		// Do this to keep things easier in the templates.
2114
		$context['theme_variant'] = '_' . $context['theme_variant'];
2115
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
2116
2117
		if (!empty($context['theme_variant']))
2118
		{
2119
			loadCSSFile('index' . $context['theme_variant'] . '.css', array(), 'smf_index' . $context['theme_variant']);
2120
			if ($context['right_to_left'])
2121
				loadCSSFile('rtl' . $context['theme_variant'] . '.css', array(), 'smf_rtl' . $context['theme_variant']);
2122
		}
2123
	}
2124
2125
	// Let's be compatible with old themes!
2126
	if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
2127
		$context['template_layers'] = array('main');
2128
2129
	$context['tabindex'] = 1;
2130
2131
	// Compatibility.
2132
	if (!isset($settings['theme_version']))
2133
		$modSettings['memberCount'] = $modSettings['totalMembers'];
2134
2135
	// Default JS variables for use in every theme
2136
	$context['javascript_vars'] = array(
2137
		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
2138
		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
2139
		'smf_images_url' => '"' . $settings['images_url'] . '"',
2140
		'smf_smileys_url' => '"' . $modSettings['smileys_url'] . '"',
2141
		'smf_scripturl' => '"' . $scripturl . '"',
2142
		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
2143
		'smf_charset' => '"' . $context['character_set'] . '"',
2144
		'smf_session_id' => '"' . $context['session_id'] . '"',
2145
		'smf_session_var' => '"' . $context['session_var'] . '"',
2146
		'smf_member_id' => $context['user']['id'],
2147
		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
2148
		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
2149
	);
2150
2151
	// Add the JQuery library to the list of files to load.
2152
	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
2153
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2154
2155
	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
2156
		loadJavaScriptFile('jquery-2.1.4.min.js', array('seed' => false), 'smf_jquery');
2157
2158
	elseif (isset($modSettings['jquery_source'], $modSettings['jquery_custom']) && $modSettings['jquery_source'] == 'custom')
2159
		loadJavaScriptFile($modSettings['jquery_custom'], array(), 'smf_jquery');
2160
2161
	// Auto loading? template_javascript() will take care of the local half of this.
2162
	else
2163
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2164
2165
	// Queue our JQuery plugins!
2166
	loadJavaScriptFile('smf_jquery_plugins.js', array('minimize' => true), 'smf_jquery_plugins');
2167
	if (!$user_info['is_guest'])
2168
	{
2169
		loadJavaScriptFile('jquery.custom-scrollbar.js', array(), 'smf_jquery_scrollbar');
2170
		loadCSSFile('jquery.custom-scrollbar.css', array('force_current' => false, 'validate' => true), 'smf_scrollbar');
2171
	}
2172
2173
	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
2174
	loadJavaScriptFile('script.js', array('defer' => false, 'minimize' => true), 'smf_script');
2175
	loadJavaScriptFile('theme.js', array('minimize' => true), 'smf_theme');
2176
2177
	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
2178
	if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2179
	{
2180
		if (isBrowser('possibly_robot'))
2181
		{
2182
			// @todo Maybe move this somewhere better?!
2183
			require_once($sourcedir . '/ScheduledTasks.php');
2184
2185
			// What to do, what to do?!
2186
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2187
				AutoTask();
2188
			else
2189
				ReduceMailQueue();
2190
		}
2191
		else
2192
		{
2193
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
2194
			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
2195
2196
			addInlineJavaScript('
2197
		function smfAutoTask()
2198
		{
2199
			$.get(smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '");
2200
		}
2201
		window.setTimeout("smfAutoTask();", 1);');
2202
		}
2203
	}
2204
2205
	// And we should probably trigger the cron too.
2206
	if (empty($modSettings['cron_is_real_cron']))
2207
	{
2208
		$ts = time();
2209
		$ts -= $ts % 15;
2210
		addInlineJavaScript('
2211
	function triggerCron()
2212
	{
2213
		$.get(' . JavaScriptEscape($boardurl) . ' + "/cron.php?ts=' . $ts . '");
2214
	}
2215
	window.setTimeout(triggerCron, 1);', true);
2216
	}
2217
2218
	// Filter out the restricted boards from the linktree
2219
	if (!$user_info['is_admin'] && !empty($board))
2220
	{
2221
		foreach ($context['linktree'] as $k => $element)
2222
		{
2223
			if (!empty($element['groups']) &&
2224
				(count(array_intersect($user_info['groups'], $element['groups'])) == 0 ||
2225
				(!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $element['deny_groups'])) != 0)))
2226
			{
2227
				$context['linktree'][$k]['name'] = $txt['restricted_board'];
2228
				$context['linktree'][$k]['extra_before'] = '<i>';
2229
				$context['linktree'][$k]['extra_after'] = '</i>';
2230
				unset($context['linktree'][$k]['url']);
2231
			}
2232
		}
2233
	}
2234
2235
	// Any files to include at this point?
2236 View Code Duplication
	if (!empty($modSettings['integrate_theme_include']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2237
	{
2238
		$theme_includes = explode(',', $modSettings['integrate_theme_include']);
2239
		foreach ($theme_includes as $include)
2240
		{
2241
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
2242
			if (file_exists($include))
2243
				require_once($include);
2244
		}
2245
	}
2246
2247
	// Call load theme integration functions.
2248
	call_integration_hook('integrate_load_theme');
2249
2250
	// We are ready to go.
2251
	$context['theme_loaded'] = true;
2252
}
2253
2254
/**
2255
 * Load a template - if the theme doesn't include it, use the default.
2256
 * What this function does:
2257
 *  - loads a template file with the name template_name from the current, default, or base theme.
2258
 *  - detects a wrong default theme directory and tries to work around it.
2259
 *
2260
 * @uses the template_include() function to include the file.
2261
 * @param string $template_name The name of the template to load
2262
 * @param array|string $style_sheets The name of a single stylesheet or an array of names of stylesheets to load
2263
 * @param bool $fatal If true, dies with an error message if the template cannot be found
2264
 * @return boolean Whether or not the template was loaded
2265
 */
2266
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
2267
{
2268
	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
2269
2270
	// Do any style sheets first, cause we're easy with those.
2271
	if (!empty($style_sheets))
2272
	{
2273
		if (!is_array($style_sheets))
2274
			$style_sheets = array($style_sheets);
2275
2276
		foreach ($style_sheets as $sheet)
2277
			loadCSSFile($sheet . '.css', array(), $sheet);
2278
	}
2279
2280
	// No template to load?
2281
	if ($template_name === false)
2282
		return true;
2283
2284
	$loaded = false;
2285
	foreach ($settings['template_dirs'] as $template_dir)
2286
	{
2287
		if (file_exists($template_dir . '/' . $template_name . '.template.php'))
2288
		{
2289
			$loaded = true;
2290
			template_include($template_dir . '/' . $template_name . '.template.php', true);
2291
			break;
2292
		}
2293
	}
2294
2295
	if ($loaded)
2296
	{
2297
		if ($db_show_debug === true)
2298
			$context['debug']['templates'][] = $template_name . ' (' . basename($template_dir) . ')';
0 ignored issues
show
Bug introduced by
The variable $template_dir seems to be defined by a foreach iteration on line 2285. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
2299
2300
		// If they have specified an initialization function for this template, go ahead and call it now.
2301
		if (function_exists('template_' . $template_name . '_init'))
2302
			call_user_func('template_' . $template_name . '_init');
2303
	}
2304
	// Hmmm... doesn't exist?!  I don't suppose the directory is wrong, is it?
2305
	elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
2306
	{
2307
		$settings['default_theme_dir'] = $boarddir . '/Themes/default';
2308
		$settings['template_dirs'][] = $settings['default_theme_dir'];
2309
2310 View Code Duplication
		if (!empty($context['user']['is_admin']) && !isset($_GET['th']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2311
		{
2312
			loadLanguage('Errors');
2313
			echo '
2314
<div class="alert errorbox">
2315
	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
2316
</div>';
2317
		}
2318
2319
		loadTemplate($template_name);
2320
	}
2321
	// Cause an error otherwise.
2322
	elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
2323
		fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
2324
	elseif ($fatal)
2325
		die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load Themes/default/%s.template.php!', (string) $template_name), 'template'));
2326
	else
2327
		return false;
2328
}
2329
2330
/**
2331
 * Load a sub-template.
2332
 * What it does:
2333
 * 	- loads the sub template specified by sub_template_name, which must be in an already-loaded template.
2334
 *  - if ?debug is in the query string, shows administrators a marker after every sub template
2335
 *	for debugging purposes.
2336
 *
2337
 * @todo get rid of reading $_REQUEST directly
2338
 *
2339
 * @param string $sub_template_name The name of the sub-template to load
2340
 * @param bool $fatal Whether to die with an error if the sub-template can't be loaded
2341
 */
2342
function loadSubTemplate($sub_template_name, $fatal = false)
2343
{
2344
	global $context, $txt, $db_show_debug;
2345
2346
	if ($db_show_debug === true)
2347
		$context['debug']['sub_templates'][] = $sub_template_name;
2348
2349
	// Figure out what the template function is named.
2350
	$theme_function = 'template_' . $sub_template_name;
2351
	if (function_exists($theme_function))
2352
		$theme_function();
2353
	elseif ($fatal === false)
2354
		fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
2355
	elseif ($fatal !== 'ignore')
2356
		die(log_error(sprintf(isset($txt['theme_template_error']) ? $txt['theme_template_error'] : 'Unable to load the %s sub template!', (string) $sub_template_name), 'template'));
2357
2358
	// Are we showing debugging for templates?  Just make sure not to do it before the doctype...
2359
	if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
2360
	{
2361
		echo '
2362
<div class="warningbox">---- ', $sub_template_name, ' ends ----</div>';
2363
	}
2364
}
2365
2366
/**
2367
 * Add a CSS file for output later
2368
 *
2369
 * @param string $fileName The name of the file to load
2370
 * @param array $params An array of parameters
2371
 * Keys are the following:
2372
 * 	- ['external'] (true/false): define if the file is a externally located file. Needs to be set to true if you are loading an external file
2373
 * 	- ['default_theme'] (true/false): force use of default theme url
2374
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the default theme if not found in the current theme
2375
 *  - ['validate'] (true/false): if true script will validate the local file exists
2376
 *  - ['rtl'] (string): additional file to load in RTL mode
2377
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2378
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2379
 * @param string $id An ID to stick on the end of the filename for caching purposes
2380
 */
2381
function loadCSSFile($fileName, $params = array(), $id = '')
2382
{
2383
	global $settings, $context, $modSettings;
2384
2385
	$params['seed'] = (!array_key_exists('seed', $params) || (array_key_exists('seed', $params) && $params['seed'] === true)) ? (array_key_exists('browser_cache', $modSettings) ? $modSettings['browser_cache'] : '') : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
2386
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2387
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2388
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2389
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2390
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2391
2392
	// If this is an external file, automatically set this to false.
2393
	if (!empty($params['external']))
2394
		$params['minimize'] = false;
2395
2396
	// Account for shorthand like admin.css?alp21 filenames
2397
	$has_seed = strpos($fileName, '.css?');
2398
	$id = empty($id) ? strtr(basename(str_replace('.css', '', $fileName)), '?', '_') : $id;
2399
2400
	// Is this a local file?
2401 View Code Duplication
	if (empty($params['external']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2402
	{
2403
		// Are we validating the the file exists?
2404
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/css/' . $fileName))
2405
		{
2406
			// Maybe the default theme has it?
2407
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/css/' . $fileName))
2408
			{
2409
				$fileUrl = $settings['default_theme_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2410
				$filePath = $settings['default_theme_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2411
			}
2412
2413
			else
2414
				$fileUrl = false;
2415
		}
2416
2417
		else
2418
		{
2419
			$fileUrl = $settings[$themeRef . '_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2420
			$filePath = $settings[$themeRef . '_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2421
		}
2422
	}
2423
2424
	// An external file doesn't have a filepath. Mock one for simplicity.
2425
	else
2426
	{
2427
		$fileUrl = $fileName;
2428
		$filePath = $fileName;
2429
	}
2430
2431
	// Add it to the array for use in the template
2432 View Code Duplication
	if (!empty($fileName))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2433
		$context['css_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
0 ignored issues
show
Bug introduced by
The variable $filePath 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...
2434
2435
	if (!empty($context['right_to_left']) && !empty($params['rtl']))
2436
		loadCSSFile($params['rtl'], array_diff_key($params, array('rtl' => 0)));
2437
}
2438
2439
/**
2440
 * Add a block of inline css code to be executed later
2441
 *
2442
 * - only use this if you have to, generally external css files are better, but for very small changes
2443
 *   or for scripts that require help from PHP/whatever, this can be useful.
2444
 * - all code added with this function is added to the same <style> tag so do make sure your css is valid!
2445
 *
2446
 * @param string $css Some css code
2447
 * @return void|bool Adds the CSS to the $context['css_header'] array or returns if no CSS is specified
2448
 */
2449
function addInlineCss($css)
2450
{
2451
	global $context;
2452
2453
	// Gotta add something...
2454
	if (empty($css))
2455
		return false;
2456
2457
	$context['css_header'][] = $css;
2458
}
2459
2460
/**
2461
 * Add a Javascript file for output later
2462
 *
2463
 * @param string $filename The name of the file to load
0 ignored issues
show
Documentation introduced by
There is no parameter named $filename. Did you maybe mean $fileName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2464
 * @param array $params An array of parameter info
2465
 * Keys are the following:
2466
 * 	- ['external'] (true/false): define if the file is a externally located file. Needs to be set to true if you are loading an external file
2467
 * 	- ['default_theme'] (true/false): force use of default theme url
2468
 * 	- ['defer'] (true/false): define if the file should load in <head> or before the closing <html> tag
2469
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the
2470
 *	default theme if not found in the current theme
2471
 *	- ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
2472
 *  - ['validate'] (true/false): if true script will validate the local file exists
2473
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2474
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2475
 *
2476
 * @param string $id An ID to stick on the end of the filename
2477
 */
2478
function loadJavaScriptFile($fileName, $params = array(), $id = '')
2479
{
2480
	global $settings, $context, $modSettings;
2481
2482
	$params['seed'] = (!array_key_exists('seed', $params) || (array_key_exists('seed', $params) && $params['seed'] === true)) ? (array_key_exists('browser_cache', $modSettings) ? $modSettings['browser_cache'] : '') : (is_string($params['seed']) ? ($params['seed'] = $params['seed'][0] === '?' ? $params['seed'] : '?' . $params['seed']) : '');
2483
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2484
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2485
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2486
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2487
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2488
2489
	// If this is an external file, automatically set this to false.
2490
	if (!empty($params['external']))
2491
		$params['minimize'] = false;
2492
2493
	// Account for shorthand like admin.js?alp21 filenames
2494
	$has_seed = strpos($fileName, '.js?');
2495
	$id = empty($id) ? strtr(basename(str_replace('.js', '', $fileName)), '?', '_') : $id;
2496
2497
	// Is this a local file?
2498 View Code Duplication
	if (empty($params['external']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2499
	{
2500
		// Are we validating it exists on disk?
2501
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/scripts/' . $fileName))
2502
		{
2503
			// Can't find it in this theme, how about the default?
2504
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/scripts/' . $fileName))
2505
			{
2506
				$fileUrl = $settings['default_theme_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2507
				$filePath = $settings['default_theme_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2508
			}
2509
2510
			else
2511
			{
2512
				$fileUrl = false;
2513
				$filePath = false;
2514
			}
2515
		}
2516
2517
		else
2518
		{
2519
			$fileUrl = $settings[$themeRef . '_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2520
			$filePath = $settings[$themeRef . '_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2521
		}
2522
	}
2523
2524
	// An external file doesn't have a filepath. Mock one for simplicity.
2525
	else
2526
	{
2527
		$fileUrl = $fileName;
2528
		$filePath = $fileName;
2529
	}
2530
2531
	// Add it to the array for use in the template
2532 View Code Duplication
	if (!empty($fileName))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2533
		$context['javascript_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2534
}
2535
2536
/**
2537
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2538
 * Cleaner and easier (for modders) than to use the function below.
2539
 *
2540
 * @param string $key The key for this variable
2541
 * @param string $value The value
2542
 * @param bool $escape Whether or not to escape the value
2543
 */
2544
function addJavaScriptVar($key, $value, $escape = false)
2545
{
2546
	global $context;
2547
2548
	if (!empty($key) && (!empty($value) || $value === '0'))
2549
		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
2550
}
2551
2552
/**
2553
 * Add a block of inline Javascript code to be executed later
2554
 *
2555
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2556
 *   or for scripts that require help from PHP/whatever, this can be useful.
2557
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2558
 *
2559
 * @param string $javascript Some JS code
2560
 * @param bool $defer Whether the script should load in <head> or before the closing <html> tag
2561
 * @return void|bool Adds the code to one of the $context['javascript_inline'] arrays or returns if no JS was specified
2562
 */
2563
function addInlineJavaScript($javascript, $defer = false)
2564
{
2565
	global $context;
2566
2567
	if (empty($javascript))
2568
		return false;
2569
2570
	$context['javascript_inline'][($defer === true ? 'defer' : 'standard')][] = $javascript;
2571
}
2572
2573
/**
2574
 * Load a language file.  Tries the current and default themes as well as the user and global languages.
2575
 *
2576
 * @param string $template_name The name of a template file
2577
 * @param string $lang A specific language to load this file from
2578
 * @param bool $fatal Whether to die with an error if it can't be loaded
2579
 * @param bool $force_reload Whether to load the file again if it's already loaded
2580
 * @return string The language actually loaded.
2581
 */
2582
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2583
{
2584
	global $user_info, $language, $settings, $context, $modSettings;
2585
	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
2586
	static $already_loaded = array();
2587
2588
	// Default to the user's language.
2589
	if ($lang == '')
2590
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2591
2592
	// Do we want the English version of language file as fallback?
2593
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2594
		loadLanguage($template_name, 'english', false);
2595
2596
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2597
		return $lang;
2598
2599
	// Make sure we have $settings - if not we're in trouble and need to find it!
2600
	if (empty($settings['default_theme_dir']))
2601
	{
2602
		require_once($sourcedir . '/ScheduledTasks.php');
2603
		loadEssentialThemeData();
2604
	}
2605
2606
	// What theme are we in?
2607
	$theme_name = basename($settings['theme_url']);
2608
	if (empty($theme_name))
2609
		$theme_name = 'unknown';
2610
2611
	// For each file open it up and write it out!
2612
	foreach (explode('+', $template_name) as $template)
2613
	{
2614
		// Obviously, the current theme is most important to check.
2615
		$attempts = array(
2616
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2617
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2618
		);
2619
2620
		// Do we have a base theme to worry about?
2621
		if (isset($settings['base_theme_dir']))
2622
		{
2623
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2624
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2625
		}
2626
2627
		// Fall back on the default theme if necessary.
2628
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2629
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2630
2631
		// Fall back on the English language if none of the preferred languages can be found.
2632
		if (!in_array('english', array($lang, $language)))
2633
		{
2634
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2635
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2636
		}
2637
2638
		// Try to find the language file.
2639
		$found = false;
2640
		foreach ($attempts as $k => $file)
2641
		{
2642
			if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2643
			{
2644
				// Include it!
2645
				template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2646
2647
				// Note that we found it.
2648
				$found = true;
2649
2650
				break;
2651
			}
2652
		}
2653
2654
		// That couldn't be found!  Log the error, but *try* to continue normally.
2655
		if (!$found && $fatal)
2656
		{
2657
			log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2658
			break;
2659
		}
2660
2661
		// For the sake of backward compatibility
2662
		if (!empty($txt['emails']))
2663
		{
2664
			foreach ($txt['emails'] as $key => $value)
2665
			{
2666
				$txt[$key . '_subject'] = $value['subject'];
2667
				$txt[$key . '_body'] = $value['body'];
2668
			}
2669
			$txt['emails'] = array();
2670
		}
2671
		// For sake of backward compatibility: $birthdayEmails is supposed to be
2672
		// empty in a normal install. If it isn't it means the forum is using
2673
		// something "old" (it may be the translation, it may be a mod) and this
2674
		// code (like the piece above) takes care of converting it to the new format
2675
		if (!empty($birthdayEmails))
2676
		{
2677
			foreach ($birthdayEmails as $key => $value)
2678
			{
2679
				$txtBirthdayEmails[$key . '_subject'] = $value['subject'];
2680
				$txtBirthdayEmails[$key . '_body'] = $value['body'];
2681
				$txtBirthdayEmails[$key . '_author'] = $value['author'];
2682
			}
2683
			$birthdayEmails = array();
2684
		}
2685
	}
2686
2687
	// Keep track of what we're up to soldier.
2688
	if ($db_show_debug === true)
2689
		$context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
2690
2691
	// Remember what we have loaded, and in which language.
2692
	$already_loaded[$template_name] = $lang;
2693
2694
	// Return the language actually loaded.
2695
	return $lang;
2696
}
2697
2698
/**
2699
 * Get all parent boards (requires first parent as parameter)
2700
 * It finds all the parents of id_parent, and that board itself.
2701
 * Additionally, it detects the moderators of said boards.
2702
 *
2703
 * @param int $id_parent The ID of the parent board
2704
 * @return array An array of information about the boards found.
2705
 */
2706
function getBoardParents($id_parent)
2707
{
2708
	global $scripturl, $smcFunc;
2709
2710
	// First check if we have this cached already.
2711
	if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
2712
	{
2713
		$boards = array();
2714
		$original_parent = $id_parent;
2715
2716
		// Loop while the parent is non-zero.
2717
		while ($id_parent != 0)
2718
		{
2719
			$result = $smcFunc['db_query']('', '
2720
				SELECT
2721
					b.id_parent, b.name, {int:board_parent} AS id_board, b.member_groups, b.deny_member_groups,
2722
					b.child_level, COALESCE(mem.id_member, 0) AS id_moderator, mem.real_name,
2723
					COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name
2724
				FROM {db_prefix}boards AS b
2725
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2726
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2727
					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
2728
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
2729
				WHERE b.id_board = {int:board_parent}',
2730
				array(
2731
					'board_parent' => $id_parent,
2732
				)
2733
			);
2734
			// In the EXTREMELY unlikely event this happens, give an error message.
2735
			if ($smcFunc['db_num_rows']($result) == 0)
2736
				fatal_lang_error('parent_not_found', 'critical');
2737
			while ($row = $smcFunc['db_fetch_assoc']($result))
2738
			{
2739
				if (!isset($boards[$row['id_board']]))
2740
				{
2741
					$id_parent = $row['id_parent'];
2742
					$boards[$row['id_board']] = array(
2743
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2744
						'name' => $row['name'],
2745
						'level' => $row['child_level'],
2746
						'groups' => explode(',', $row['member_groups']),
2747
						'deny_groups' => explode(',', $row['deny_member_groups']),
2748
						'moderators' => array(),
2749
						'moderator_groups' => array()
2750
					);
2751
				}
2752
				// If a moderator exists for this board, add that moderator for all children too.
2753 View Code Duplication
				if (!empty($row['id_moderator']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2754
					foreach ($boards as $id => $dummy)
2755
					{
2756
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2757
							'id' => $row['id_moderator'],
2758
							'name' => $row['real_name'],
2759
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2760
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2761
						);
2762
					}
2763
2764
				// If a moderator group exists for this board, add that moderator group for all children too
2765 View Code Duplication
				if (!empty($row['id_moderator_group']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2766
					foreach ($boards as $id => $dummy)
2767
					{
2768
						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
2769
							'id' => $row['id_moderator_group'],
2770
							'name' => $row['group_name'],
2771
							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
2772
							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
2773
						);
2774
					}
2775
			}
2776
			$smcFunc['db_free_result']($result);
2777
		}
2778
2779
		cache_put_data('board_parents-' . $original_parent, $boards, 480);
2780
	}
2781
2782
	return $boards;
2783
}
2784
2785
/**
2786
 * Attempt to reload our known languages.
2787
 * It will try to choose only utf8 or non-utf8 languages.
2788
 *
2789
 * @param bool $use_cache Whether or not to use the cache
2790
 * @param bool $favor_utf8 Whether or not to favor UTF-8 files
2791
 * @return array An array of information about available languages
2792
 */
2793
function getLanguages($use_cache = true, $favor_utf8 = true)
2794
{
2795
	global $context, $smcFunc, $settings, $modSettings;
2796
2797
	// Either we don't use the cache, or its expired.
2798
	if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2799
	{
2800
		// If we don't have our ucwords function defined yet, let's load the settings data.
2801
		if (empty($smcFunc['ucwords']))
2802
			reloadSettings();
2803
2804
		// If we don't have our theme information yet, let's get it.
2805
		if (empty($settings['default_theme_dir']))
2806
			loadTheme(0, false);
2807
2808
		// Default language directories to try.
2809
		$language_directories = array(
2810
			$settings['default_theme_dir'] . '/languages',
2811
		);
2812
		if (!empty($settings['actual_theme_dir']) && $settings['actual_theme_dir'] != $settings['default_theme_dir'])
2813
			$language_directories[] = $settings['actual_theme_dir'] . '/languages';
2814
2815
		// We possibly have a base theme directory.
2816
		if (!empty($settings['base_theme_dir']))
2817
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2818
2819
		// Remove any duplicates.
2820
		$language_directories = array_unique($language_directories);
2821
2822
		// Get a list of languages.
2823
		$langList = !empty($modSettings['langList']) ? json_decode($modSettings['langList'], true) : array();
2824
		$langList = is_array($langList) ? $langList : false;
2825
2826
		$catchLang = array();
2827
2828
		foreach ($language_directories as $language_dir)
2829
		{
2830
			// Can't look in here... doesn't exist!
2831
			if (!file_exists($language_dir))
2832
				continue;
2833
2834
			$dir = dir($language_dir);
2835
			while ($entry = $dir->read())
2836
			{
2837
				// Look for the index language file....
2838
				if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches))
2839
					continue;
2840
2841
				if (!empty($langList) && !empty($langList[$matches[1]]))
2842
					$langName = $langList[$matches[1]];
2843
2844
				else
2845
				{
2846
					$langName = $smcFunc['ucwords'](strtr($matches[1], array('_' => ' ')));
2847
2848
					// Get the line we need.
2849
					$fp = @fopen($language_dir .'/'. $entry);
2850
2851
					// Yay!
2852
					if ($fp)
2853
					{
2854
						while (($line = fgets($fp)) !== false)
2855
						{
2856
							preg_match('~\$txt\[\'native_name\'\] = \'(.+)\'\;~', $line, $matchNative);
2857
2858
							// Set the language's name.
2859
							if (!empty($matchNative) && !empty($matchNative[1]))
2860
							{
2861
								$langName = un_htmlspecialchars($matchNative[1]);
2862
								break;
2863
							}
2864
						}
2865
2866
						fclose($fp);
2867
					}
2868
2869
					// Catch the language name.
2870
					$catchLang[$matches[1]] = $langName;
2871
				}
2872
2873
				// Build this language entry.
2874
				$context['languages'][$matches[1]] = array(
2875
					'name' => $langName,
2876
					'selected' => false,
2877
					'filename' => $matches[1],
2878
					'location' => $language_dir . '/index.' . $matches[1] . '.php',
2879
				);
2880
2881
				$indexFile = '';
0 ignored issues
show
Unused Code introduced by
$indexFile is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2882
			}
2883
			$dir->close();
2884
		}
2885
2886
		// Do we need to store the lang list?
2887
		if (empty($langList))
2888
			updateSettings(array('langList' => json_encode($catchLang)));
2889
2890
		// Favoring UTF8? Then prevent us from selecting non-UTF8 versions.
2891
		if ($favor_utf8)
2892
		{
2893
			foreach ($context['languages'] as $lang)
0 ignored issues
show
Bug introduced by
The expression $context['languages'] of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2894
				if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8']))
2895
					unset($context['languages'][$lang['filename']]);
2896
		}
2897
2898
		// Let's cash in on this deal.
2899
		if (!empty($modSettings['cache_enable']))
2900
			cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
2901
	}
2902
2903
	return $context['languages'];
2904
}
2905
2906
/**
2907
 * Replace all vulgar words with respective proper words. (substring or whole words..)
2908
 * What this function does:
2909
 *  - it censors the passed string.
2910
 *  - if the theme setting allow_no_censored is on, and the theme option
2911
 *	show_no_censored is enabled, does not censor, unless force is also set.
2912
 *  - it caches the list of censored words to reduce parsing.
2913
 *
2914
 * @param string &$text The text to censor
2915
 * @param bool $force Whether to censor the text regardless of settings
2916
 * @return string The censored text
2917
 */
2918
function censorText(&$text, $force = false)
2919
{
2920
	global $modSettings, $options, $txt;
2921
	static $censor_vulgar = null, $censor_proper;
2922
2923
	if ((!empty($options['show_no_censored']) && !empty($modSettings['allow_no_censored']) && !$force) || empty($modSettings['censor_vulgar']) || trim($text) === '')
2924
		return $text;
2925
2926
	// If they haven't yet been loaded, load them.
2927
	if ($censor_vulgar == null)
2928
	{
2929
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
2930
		$censor_proper = explode("\n", $modSettings['censor_proper']);
2931
2932
		// Quote them for use in regular expressions.
2933
		if (!empty($modSettings['censorWholeWord']))
2934
		{
2935
			for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
2936
			{
2937
				$censor_vulgar[$i] = str_replace(array('\\\\\\*', '\\*', '&', '\''), array('[*]', '[^\s]*?', '&amp;', '&#039;'), preg_quote($censor_vulgar[$i], '/'));
2938
				$censor_vulgar[$i] = '/(?<=^|\W)' . $censor_vulgar[$i] . '(?=$|\W)/' . (empty($modSettings['censorIgnoreCase']) ? '' : 'i') . ((empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set']) === 'UTF-8' ? 'u' : '');
2939
2940
				// @todo I'm thinking the old way is some kind of bug and this is actually fixing it.
2941
				//if (strpos($censor_vulgar[$i], '\'') !== false)
2942
					//$censor_vulgar[$i] = str_replace('\'', '&#039;', $censor_vulgar[$i]);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2943
			}
2944
		}
2945
	}
2946
2947
	// Censoring isn't so very complicated :P.
2948
	if (empty($modSettings['censorWholeWord']))
2949
	{
2950
		$func = !empty($modSettings['censorIgnoreCase']) ? 'str_ireplace' : 'str_replace';
2951
		$text = $func($censor_vulgar, $censor_proper, $text);
2952
	}
2953
	else
2954
		$text = preg_replace($censor_vulgar, $censor_proper, $text);
2955
2956
	return $text;
2957
}
2958
2959
/**
2960
 * Load the template/language file using eval or require? (with eval we can show an error message!)
2961
 * 	- loads the template or language file specified by filename.
2962
 * 	- uses eval unless disableTemplateEval is enabled.
2963
 * 	- outputs a parse error if the file did not exist or contained errors.
2964
 * 	- attempts to detect the error and line, and show detailed information.
2965
 *
2966
 * @param string $filename The name of the file to include
2967
 * @param bool $once If true only includes the file once (like include_once)
2968
 */
2969
function template_include($filename, $once = false)
2970
{
2971
	global $context, $settings, $txt, $scripturl, $modSettings;
2972
	global $boardurl, $boarddir, $sourcedir;
2973
	global $maintenance, $mtitle, $mmessage;
2974
	static $templates = array();
2975
2976
	// We want to be able to figure out any errors...
2977
	@ini_set('track_errors', '1');
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...
2978
2979
	// Don't include the file more than once, if $once is true.
2980
	if ($once && in_array($filename, $templates))
2981
		return;
2982
	// Add this file to the include list, whether $once is true or not.
2983
	else
2984
		$templates[] = $filename;
2985
2986
	// Are we going to use eval?
2987
	if (empty($modSettings['disableTemplateEval']))
2988
	{
2989
		$file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
0 ignored issues
show
Coding Style introduced by
The function template_include() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2990
		$settings['current_include_filename'] = $filename;
2991
	}
2992
	else
2993
	{
2994
		$file_found = file_exists($filename);
2995
2996
		if ($once && $file_found)
2997
			require_once($filename);
2998
		elseif ($file_found)
2999
			require($filename);
3000
	}
3001
3002
	if ($file_found !== true)
3003
	{
3004
		ob_end_clean();
3005
		if (!empty($modSettings['enableCompressedOutput']))
3006
			@ob_start('ob_gzhandler');
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...
3007
		else
3008
			ob_start();
3009
3010 View Code Duplication
		if (isset($_GET['debug']))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3011
			header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
3012
3013
		// Don't cache error pages!!
3014
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
3015
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
3016
		header('Cache-Control: no-cache');
3017
3018
		if (!isset($txt['template_parse_error']))
3019
		{
3020
			$txt['template_parse_error'] = 'Template Parse Error!';
3021
			$txt['template_parse_error_message'] = 'It seems something has gone sour on the forum with the template system.  This problem should only be temporary, so please come back later and try again.  If you continue to see this message, please contact the administrator.<br><br>You can also try <a href="javascript:location.reload();">refreshing this page</a>.';
3022
			$txt['template_parse_error_details'] = 'There was a problem loading the <pre><strong>%1$s</strong></pre> template or language file.  Please check the syntax and try again - remember, single quotes (<pre>\'</pre>) often have to be escaped with a slash (<pre>\\</pre>).  To see more specific error information from PHP, try <a href="' . $boardurl . '%1$s" class="extern">accessing the file directly</a>.<br><br>You may want to try to <a href="javascript:location.reload();">refresh this page</a> or <a href="' . $scripturl . '?theme=1">use the default theme</a>.';
3023
			$txt['template_parse_errmsg'] = 'Unfortunately more information is not available at this time as to exactly what is wrong.';
3024
		}
3025
3026
		// First, let's get the doctype and language information out of the way.
3027
		echo '<!DOCTYPE html>
3028
<html', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
3029
	<head>';
3030
		if (isset($context['character_set']))
3031
			echo '
3032
		<meta charset="', $context['character_set'], '">';
3033
3034
		if (!empty($maintenance) && !allowedTo('admin_forum'))
3035
			echo '
3036
		<title>', $mtitle, '</title>
3037
	</head>
3038
	<body>
3039
		<h3>', $mtitle, '</h3>
3040
		', $mmessage, '
3041
	</body>
3042
</html>';
3043
		elseif (!allowedTo('admin_forum'))
3044
			echo '
3045
		<title>', $txt['template_parse_error'], '</title>
3046
	</head>
3047
	<body>
3048
		<h3>', $txt['template_parse_error'], '</h3>
3049
		', $txt['template_parse_error_message'], '
3050
	</body>
3051
</html>';
3052
		else
3053
		{
3054
			require_once($sourcedir . '/Subs-Package.php');
3055
3056
			$error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3057
			if (empty($error) && ini_get('track_errors') && !empty($php_errormsg))
3058
				$error = $php_errormsg;
3059
			if (empty($error))
3060
				$error = $txt['template_parse_errmsg'];
3061
3062
			$error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
3063
3064
			echo '
3065
		<title>', $txt['template_parse_error'], '</title>
3066
	</head>
3067
	<body>
3068
		<h3>', $txt['template_parse_error'], '</h3>
3069
		', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3070
3071
			if (!empty($error))
3072
				echo '
3073
		<hr>
3074
3075
		<div style="margin: 0 20px;"><pre>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</pre></div>';
3076
3077
			// I know, I know... this is VERY COMPLICATED.  Still, it's good.
3078
			if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
3079
			{
3080
				$data = file($filename);
3081
				$data2 = highlight_php_code(implode('', $data));
3082
				$data2 = preg_split('~\<br( /)?\>~', $data2);
3083
3084
				// Fix the PHP code stuff...
3085
				if (!isBrowser('gecko'))
3086
					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
3087
				else
3088
					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
3089
3090
				// Now we get to work around a bug in PHP where it doesn't escape <br>s!
3091
				$j = -1;
3092
				foreach ($data as $line)
3093
				{
3094
					$j++;
3095
3096
					if (substr_count($line, '<br>') == 0)
3097
						continue;
3098
3099
					$n = substr_count($line, '<br>');
3100
					for ($i = 0; $i < $n; $i++)
3101
					{
3102
						$data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
3103
						unset($data2[$j + $i + 1]);
3104
					}
3105
					$j += $n;
3106
				}
3107
				$data2 = array_values($data2);
3108
				array_unshift($data2, '');
3109
3110
				echo '
3111
		<div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
3112
3113
				// Figure out what the color coding was before...
3114
				$line = max($match[1] - 9, 1);
3115
				$last_line = '';
3116
				for ($line2 = $line - 1; $line2 > 1; $line2--)
3117
					if (strpos($data2[$line2], '<') !== false)
3118
					{
3119
						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
3120
							$last_line = $color_match[1];
3121
						break;
3122
					}
3123
3124
				// Show the relevant lines...
3125
				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
3126
				{
3127
					if ($line == $match[1])
3128
						echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
3129
3130
					echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
3131
					if (isset($data2[$line]) && $data2[$line] != '')
3132
						echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
3133
3134
					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
3135
					{
3136
						$last_line = $color_match[1];
3137
						echo '</', substr($last_line, 1, 4), '>';
3138
					}
3139
					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
3140
						$last_line = '';
3141
					elseif ($last_line != '' && $data2[$line] != '')
3142
						echo '</', substr($last_line, 1, 4), '>';
3143
3144
					if ($line == $match[1])
3145
						echo '</pre></div><pre style="margin: 0;">';
3146
					else
3147
						echo "\n";
3148
				}
3149
3150
				echo '</pre></div>';
3151
			}
3152
3153
			echo '
3154
	</body>
3155
</html>';
3156
		}
3157
3158
		die;
3159
	}
3160
}
3161
3162
/**
3163
 * Initialize a database connection.
3164
 */
3165
function loadDatabase()
3166
{
3167
	global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
3168
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix, $db_port;
3169
3170
	// Figure out what type of database we are using.
3171
	if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
3172
		$db_type = 'mysql';
3173
3174
	// Load the file for the database.
3175
	require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
3176
3177
	$db_options = array();
3178
3179
	// Add in the port if needed
3180
	if (!empty($db_port))
3181
		$db_options['port'] = $db_port;
3182
3183
	// If we are in SSI try them first, but don't worry if it doesn't work, we have the normal username and password we can use.
3184
	if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
3185
	{
3186
		$options = array_merge($db_options, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
3187
3188
		$db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, $options);
3189
	}
3190
3191
	// Either we aren't in SSI mode, or it failed.
3192
	if (empty($db_connection))
3193
	{
3194
		$options = array_merge($db_options, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
3195
3196
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
3197
	}
3198
3199
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
3200
	if (!$db_connection)
3201
		display_db_error();
3202
3203
	// If in SSI mode fix up the prefix.
3204
	if (SMF == 'SSI')
3205
		db_fix_prefix($db_prefix, $db_name);
0 ignored issues
show
Unused Code introduced by
The call to the function db_fix_prefix() seems unnecessary as the function has no side-effects.
Loading history...
3206
}
3207
3208
/**
3209
 * Try to load up a supported caching method. This is saved in $cacheAPI if we are not overriding it.
3210
 *
3211
 * @param string $overrideCache Try to use a different cache method other than that defined in $cache_accelerator.
3212
 * @param string $fallbackSMF Use the default SMF method if the accelerator fails.
3213
 * @return object A object of $cacheAPI.
3214
*/
3215
function loadCacheAccelerator($overrideCache = null, $fallbackSMF = true)
3216
{
3217
	global $sourcedir, $cacheAPI, $cache_accelerator;
3218
3219
	// Not overriding this and we have a cacheAPI, send it back.
3220
	if (empty($overrideCache) && is_object($cacheAPI))
3221
		return $cacheAPI;
3222
	elseif (is_null($cacheAPI))
3223
		$cacheAPI = false;
3224
3225
	// Make sure our class is in session.
3226
	require_once($sourcedir . '/Class-CacheAPI.php');
3227
3228
	// What accelerator we are going to try.
3229
	$tryAccelerator = !empty($overrideCache) ? $overrideCache : !empty($cache_accelerator) ? $cache_accelerator : 'smf';
3230
	$tryAccelerator = strtolower($tryAccelerator);
3231
3232
	// Do some basic tests.
3233
	if (file_exists($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php'))
3234
	{
3235
		require_once($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php');
3236
3237
		$cache_class_name = $tryAccelerator . '_cache';
3238
		$testAPI = new $cache_class_name();
3239
3240
		// No Support?  NEXT!
3241
		if (!$testAPI->isSupported())
3242
		{
3243
			// Can we save ourselves?
3244
			if (!empty($fallbackSMF) && is_null($overrideCache) && $tryAccelerator != 'smf')
3245
				return loadCacheAccelerator(null, false);
3246
			return false;
3247
		}
3248
3249
		// Connect up to the accelerator.
3250
		$testAPI->connect();
3251
3252
		// Don't set this if we are overriding the cache.
3253
		if (is_null($overrideCache))
3254
		{
3255
			$cacheAPI = $testAPI;
3256
			return $cacheAPI;
3257
		}
3258
		else
3259
			return $testAPI;
3260
	}
3261
}
3262
3263
/**
3264
 * Try to retrieve a cache entry. On failure, call the appropriate function.
3265
 *
3266
 * @param string $key The key for this entry
3267
 * @param string $file The file associated with this entry
3268
 * @param string $function The function to call
3269
 * @param array $params Parameters to be passed to the specified function
3270
 * @param int $level The cache level
3271
 * @return string The cached data
3272
 */
3273
function cache_quick_get($key, $file, $function, $params, $level = 1)
3274
{
3275
	global $modSettings, $sourcedir;
3276
3277
	// @todo Why are we doing this if caching is disabled?
3278
3279
	if (function_exists('call_integration_hook'))
3280
		call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level));
3281
3282
	/* Refresh the cache if either:
3283
		1. Caching is disabled.
3284
		2. The cache level isn't high enough.
3285
		3. The item has not been cached or the cached item expired.
3286
		4. The cached item has a custom expiration condition evaluating to true.
3287
		5. The expire time set in the cache item has passed (needed for Zend).
3288
	*/
3289
	if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < $level || !is_array($cache_block = cache_get_data($key, 3600)) || (!empty($cache_block['refresh_eval']) && eval($cache_block['refresh_eval'])) || (!empty($cache_block['expires']) && $cache_block['expires'] < time()))
0 ignored issues
show
Coding Style introduced by
The function cache_quick_get() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
3290
	{
3291
		require_once($sourcedir . '/' . $file);
3292
		$cache_block = call_user_func_array($function, $params);
3293
3294
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
3295
			cache_put_data($key, $cache_block, $cache_block['expires'] - time());
3296
	}
3297
3298
	// Some cached data may need a freshening up after retrieval.
3299
	if (!empty($cache_block['post_retri_eval']))
3300
		eval($cache_block['post_retri_eval']);
0 ignored issues
show
Coding Style introduced by
The function cache_quick_get() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
3301
3302
	if (function_exists('call_integration_hook'))
3303
		call_integration_hook('post_cache_quick_get', array(&$cache_block));
3304
3305
	return $cache_block['data'];
3306
}
3307
3308
/**
3309
 * Puts value in the cache under key for ttl seconds.
3310
 *
3311
 * - It may "miss" so shouldn't be depended on
3312
 * - Uses the cache engine chosen in the ACP and saved in settings.php
3313
 * - It supports:
3314
 *	 Xcache: http://xcache.lighttpd.net/wiki/XcacheApi
3315
 *	 memcache: http://www.php.net/memcache
3316
 *	 APC: http://www.php.net/apc
3317
 *   APCu: http://www.php.net/book.apcu
3318
 *	 Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm
3319
 *	 Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm
3320
 *
3321
 * @param string $key A key for this value
3322
 * @param mixed $value The data to cache
3323
 * @param int $ttl How long (in seconds) the data should be cached for
3324
 */
3325
function cache_put_data($key, $value, $ttl = 120)
3326
{
3327
	global $boardurl, $modSettings, $cache_enable, $cacheAPI;
3328
	global $cache_hits, $cache_count, $db_show_debug;
3329
3330
	if (empty($cache_enable) || empty($cacheAPI))
3331
		return;
3332
3333
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3334
	if (isset($db_show_debug) && $db_show_debug === true)
3335
	{
3336
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(json_encode($value)));
3337
		$st = microtime();
3338
	}
3339
3340
	// The API will handle the rest.
3341
	$value = $value === null ? null : json_encode($value);
3342
	$cacheAPI->putData($key, $value, $ttl);
3343
3344
	if (function_exists('call_integration_hook'))
3345
		call_integration_hook('cache_put_data', array(&$key, &$value, &$ttl));
3346
3347 View Code Duplication
	if (isset($db_show_debug) && $db_show_debug === true)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3348
		$cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
0 ignored issues
show
Bug introduced by
The variable $st 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...
3349
}
3350
3351
/**
3352
 * Gets the value from the cache specified by key, so long as it is not older than ttl seconds.
3353
 * - It may often "miss", so shouldn't be depended on.
3354
 * - It supports the same as cache_put_data().
3355
 *
3356
 * @param string $key The key for the value to retrieve
3357
 * @param int $ttl The maximum age of the cached data
3358
 * @return string The cached data or null if nothing was loaded
3359
 */
3360
function cache_get_data($key, $ttl = 120)
3361
{
3362
	global $boardurl, $modSettings, $cache_enable, $cacheAPI;
3363
	global $cache_hits, $cache_count, $cache_misses, $cache_count_misses, $db_show_debug;
3364
3365
	if (empty($cache_enable) || empty($cacheAPI))
3366
		return;
3367
3368
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3369
	if (isset($db_show_debug) && $db_show_debug === true)
3370
	{
3371
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
3372
		$st = microtime();
3373
		$original_key = $key;
3374
	}
3375
3376
	// Ask the API to get the data.
3377
	$value = $cacheAPI->getData($key, $ttl);
3378
3379
	if (isset($db_show_debug) && $db_show_debug === true)
3380
	{
3381
		$cache_hits[$cache_count]['t'] = array_sum(explode(' ', microtime())) - array_sum(explode(' ', $st));
0 ignored issues
show
Bug introduced by
The variable $st 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...
3382
		$cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
3383
3384
		if (empty($value))
3385
		{
3386
			if (!is_array($cache_misses))
3387
				$cache_misses = array();
3388
3389
			$cache_count_misses = isset($cache_count_misses) ? $cache_count_misses + 1 : 1;
3390
			$cache_misses[$cache_count_misses] = array('k' => $original_key, 'd' => 'get');
0 ignored issues
show
Bug introduced by
The variable $original_key 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...
3391
		}
3392
	}
3393
3394
	if (function_exists('call_integration_hook') && isset($value))
3395
		call_integration_hook('cache_get_data', array(&$key, &$ttl, &$value));
3396
3397
	return empty($value) ? null : smf_json_decode($value, true);
3398
}
3399
3400
/**
3401
 * Empty out the cache in use as best it can
3402
 *
3403
 * It may only remove the files of a certain type (if the $type parameter is given)
3404
 * Type can be user, data or left blank
3405
 * 	- user clears out user data
3406
 *  - data clears out system / opcode data
3407
 *  - If no type is specified will perform a complete cache clearing
3408
 * For cache engines that do not distinguish on types, a full cache flush will be done
3409
 *
3410
 * @param string $type The cache type ('memcached', 'apc', 'xcache', 'zend' or something else for SMF's file cache)
3411
 */
3412
function clean_cache($type = '')
0 ignored issues
show
Best Practice introduced by
The function clean_cache() has been defined more than once; this definition is ignored, only the first definition in other/upgrade.php (L183-204) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
3413
{
3414
	global $cachedir, $sourcedir, $modSettings, $cacheAPI;
3415
3416
	// If we can't get to the API, can't do this.
3417
	if (empty($cacheAPI))
3418
		return false;
3419
3420
	// Ask the API to do the heavy lifting. cleanCache also calls invalidateCache to be sure.
3421
	$value = $cacheAPI->cleanCache($type);
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3422
3423
	call_integration_hook('integrate_clean_cache');
3424
	clearstatcache();
3425
}
3426
3427
/**
3428
 * Helper function to set an array of data for an user's avatar.
3429
 *
3430
 * Makes assumptions based on the data provided, the following keys are required:
3431
 * - avatar The raw "avatar" column in members table
3432
 * - email The user's email. Used to get the gravatar info
3433
 * - filename The attachment filename
3434
 *
3435
 * @param array $data An array of raw info
3436
 * @return array An array of avatar data
3437
 */
3438
function set_avatar_data($data = array())
3439
{
3440
	global $modSettings, $boardurl, $smcFunc, $image_proxy_enabled, $image_proxy_secret;
3441
3442
	// Come on!
3443
	if (empty($data))
3444
		return array();
3445
3446
	// Set a nice default var.
3447
	$image = '';
3448
3449
	// Gravatar has been set as mandatory!
3450
	if (!empty($modSettings['gravatarOverride']))
3451
	{
3452
		if (!empty($modSettings['gravatarAllowExtraEmail']) && !empty($data['avatar']) && stristr($data['avatar'], 'gravatar://'))
3453
			$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3454
3455
		else if (!empty($data['email']))
3456
			$image = get_gravatar_url($data['email']);
3457
	}
3458
3459
	// Look if the user has a gravatar field or has set an external url as avatar.
3460
	else
3461
	{
3462
		// So it's stored in the member table?
3463
		if (!empty($data['avatar']))
3464
		{
3465
			// Gravatar.
3466
			if (stristr($data['avatar'], 'gravatar://'))
3467
			{
3468
				if ($data['avatar'] == 'gravatar://')
3469
					$image = get_gravatar_url($data['email']);
3470
3471
				elseif (!empty($modSettings['gravatarAllowExtraEmail']))
3472
					$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3473
			}
3474
3475
			// External url.
3476
			else
3477
			{
3478
				// Using ssl?
3479
				if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($data['avatar'], 'http://') !== false)
3480
					$image = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($data['avatar']) . '&hash=' . md5($data['avatar'] . $image_proxy_secret);
3481
3482
				// Just a plain external url.
3483
				else
3484
					$image = (stristr($data['avatar'], 'http://') || stristr($data['avatar'], 'https://')) ? $data['avatar'] : $modSettings['avatar_url'] . '/' . $data['avatar'];
3485
			}
3486
		}
3487
3488
		// Perhaps this user has an attachment as avatar...
3489
		else if (!empty($data['filename']))
3490
			$image = $modSettings['custom_avatar_url'] . '/' . $data['filename'];
3491
3492
		// Right... no avatar... use our default image.
3493
		else
3494
			$image = $modSettings['avatar_url'] . '/default.png';
3495
	}
3496
3497
	call_integration_hook('integrate_set_avatar_data', array(&$image, &$data));
3498
3499
	// At this point in time $image has to be filled unless you chose to force gravatar and the user doesn't have the needed data to retrieve it... thus a check for !empty() is still needed.
3500
	if (!empty($image))
3501
		return array(
3502
			'name' => !empty($data['avatar']) ? $data['avatar'] : '',
3503
			'image' => '<img class="avatar" src="' . $image . '" />',
3504
			'href' => $image,
3505
			'url' => $image,
3506
		);
3507
3508
	// Fallback to make life easier for everyone...
3509
	else
3510
		return array(
3511
			'name' => '',
3512
			'image' => '',
3513
			'href' => '',
3514
			'url' => '',
3515
		);
3516
}
3517
3518
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

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

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

Loading history...