Completed
Branch release-2.1 (e49a83)
by Mert
04:10
created

Load.php ➔ loadCacheAccelerator()   C

Complexity

Conditions 12
Paths 41

Size

Total Lines 47
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 23
nc 41
nop 2
dl 0
loc 47
rs 5.1384
c 0
b 0
f 0

How to fix   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
		while ($row = $smcFunc['db_fetch_row']($request))
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']))
225
		date_default_timezone_set($modSettings['default_timezone']);
226
227
	// Check the load averages?
228
	if (!empty($modSettings['loadavg_enable']))
229
	{
230
		if (($modSettings['load_average'] = cache_get_data('loadavg', 90)) == null)
231
		{
232
			$modSettings['load_average'] = @file_get_contents('/proc/loadavg');
233
			if (!empty($modSettings['load_average']) && preg_match('~^([^ ]+?) ([^ ]+?) ([^ ]+)~', $modSettings['load_average'], $matches) != 0)
234
				$modSettings['load_average'] = (float) $matches[1];
235 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...
236
				$modSettings['load_average'] = (float) $matches[1];
237
			else
238
				unset($modSettings['load_average']);
239
240
			if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
241
				cache_put_data('loadavg', $modSettings['load_average'], 90);
242
		}
243
244
		if (!empty($modSettings['load_average']) || $modSettings['load_average'] === 0.0)
245
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
246
247
		if (!empty($modSettings['loadavg_forum']) && !empty($modSettings['load_average']) && $modSettings['load_average'] >= $modSettings['loadavg_forum'])
248
			display_loadavg_error();
249
	}
250
251
	// Is post moderation alive and well? Everywhere else assumes this has been defined, so let's make sure it is.
252
	$modSettings['postmod_active'] = !empty($modSettings['postmod_active']);
253
254
	// Here to justify the name of this function. :P
255
	// It should be added to the install and upgrade scripts.
256
	// But since the converters need to be updated also. This is easier.
257
	if (empty($modSettings['currentAttachmentUploadDir']))
258
	{
259
		updateSettings(array(
260
			'attachmentUploadDir' => json_encode(array(1 => $modSettings['attachmentUploadDir'])),
261
			'currentAttachmentUploadDir' => 1,
262
		));
263
	}
264
265
	// Integration is cool.
266
	if (defined('SMF_INTEGRATION_SETTINGS'))
267
	{
268
		$integration_settings = smf_json_decode(SMF_INTEGRATION_SETTINGS, true);
269
		foreach ($integration_settings as $hook => $function)
270
			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...
271
	}
272
273
	// Any files to pre include?
274 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...
275
	{
276
		$pre_includes = explode(',', $modSettings['integrate_pre_include']);
277
		foreach ($pre_includes as $include)
278
		{
279
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir));
280
			if (file_exists($include))
281
				require_once($include);
282
		}
283
	}
284
285
	// This determines the server... not used in many places, except for login fixing.
286
	$context['server'] = array(
287
		'is_iis' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false,
288
		'is_apache' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false,
289
		'is_litespeed' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false,
290
		'is_lighttpd' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false,
291
		'is_nginx' => isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false,
292
		'is_cgi' => isset($_SERVER['SERVER_SOFTWARE']) && strpos(php_sapi_name(), 'cgi') !== false,
293
		'is_windows' => strpos(PHP_OS, 'WIN') === 0,
294
		'iso_case_folding' => ord(strtolower(chr(138))) === 154,
295
	);
296
	// A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
297
	$context['server']['needs_login_fix'] = $context['server']['is_cgi'] && $context['server']['is_iis'];
298
299
	// Define a list of icons used across multiple places.
300
	$context['stable_icons'] = array('xx', 'thumbup', 'thumbdown', 'exclamation', 'question', 'lamp', 'smiley', 'angry', 'cheesy', 'grin', 'sad', 'wink', 'poll', 'moved', 'recycled', 'clip');
301
302
	// Define an array for custom profile fields placements.
303
	$context['cust_profile_fields_placement'] = array(
304
		'standard',
305
		'icons',
306
		'above_signature',
307
		'below_signature',
308
		'below_avatar',
309
		'above_member',
310
		'bottom_poster',
311
	);
312
313
	// Define an array for content-related <meta> elements (e.g. description, keywords, Open Graph) for the HTML head.
314
	$context['meta_tags'] = array();
315
316
	// Define an array of allowed HTML tags.
317
	$context['allowed_html_tags'] = array(
318
		'<img>',
319
		'<div>',
320
	);
321
322
	// These are the only valid image types for SMF, by default anyway.
323
	$context['validImageTypes'] = array(
324
		1 => 'gif',
325
		2 => 'jpeg',
326
		3 => 'png',
327
		5 => 'psd',
328
		6 => 'bmp',
329
		7 => 'tiff',
330
		8 => 'tiff',
331
		9 => 'jpeg',
332
		14 => 'iff'
333
	);
334
335
	// Define a list of allowed tags for descriptions.
336
	$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',);
337
338
	// Get an error count, if necessary
339
	if (!isset($context['num_errors']))
340
	{
341
		$query = $smcFunc['db_query']('', '
342
			SELECT COUNT(id_error)
343
			FROM {db_prefix}log_errors',
344
			array()
345
		);
346
347
		list($context['num_errors']) = $smcFunc['db_fetch_row']($query);
348
		$smcFunc['db_free_result']($query);
349
	}
350
351
	// Call pre load integration functions.
352
	call_integration_hook('integrate_pre_load');
353
}
354
355
/**
356
 * Load all the important user information.
357
 * What it does:
358
 * 	- sets up the $user_info array
359
 * 	- assigns $user_info['query_wanna_see_board'] for what boards the user can see.
360
 * 	- first checks for cookie or integration validation.
361
 * 	- uses the current session if no integration function or cookie is found.
362
 * 	- checks password length, if member is activated and the login span isn't over.
363
 * 		- if validation fails for the user, $id_member is set to 0.
364
 * 		- updates the last visit time when needed.
365
 */
366
function loadUserSettings()
367
{
368
	global $modSettings, $user_settings, $sourcedir, $smcFunc;
369
	global $cookiename, $user_info, $language, $context, $image_proxy_enabled, $image_proxy_secret, $boardurl;
370
371
	// Check first the integration, then the cookie, and last the session.
372
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
373
	{
374
		$id_member = 0;
375
		foreach ($integration_ids as $integration_id)
376
		{
377
			$integration_id = (int) $integration_id;
378
			if ($integration_id > 0)
379
			{
380
				$id_member = $integration_id;
381
				$already_verified = true;
382
				break;
383
			}
384
		}
385
	}
386
	else
387
		$id_member = 0;
388
389
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
390
	{
391
		$cookie_data = smf_json_decode($_COOKIE[$cookiename], true, false);
392
393
		if (empty($cookie_data))
394
			$cookie_data = safe_unserialize($_COOKIE[$cookiename]);
395
396
		list ($id_member, $password) = $cookie_data;
397
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
398
	}
399
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && ($_SESSION['USER_AGENT'] == $_SERVER['HTTP_USER_AGENT'] || !empty($modSettings['disableCheckUA'])))
400
	{
401
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
402
		$cookie_data = smf_json_decode($_SESSION['login_' . $cookiename]);
403
404
		if (empty($cookie_data))
405
			$cookie_data = safe_unserialize($_SESSION['login_' . $cookiename]);
406
407
		list ($id_member, $password, $login_span) = $cookie_data;
408
		$id_member = !empty($id_member) && strlen($password) == 128 && $login_span > time() ? (int) $id_member : 0;
409
	}
410
411
	// Only load this stuff if the user isn't a guest.
412
	if ($id_member != 0)
413
	{
414
		// Is the member data cached?
415
		if (empty($modSettings['cache_enable']) || $modSettings['cache_enable'] < 2 || ($user_settings = cache_get_data('user_settings-' . $id_member, 60)) == null)
416
		{
417
			$request = $smcFunc['db_query']('', '
418
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
419
				FROM {db_prefix}members AS mem
420
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
421
				WHERE mem.id_member = {int:id_member}
422
				LIMIT 1',
423
				array(
424
					'id_member' => $id_member,
425
				)
426
			);
427
			$user_settings = $smcFunc['db_fetch_assoc']($request);
428
			$smcFunc['db_free_result']($request);
429
430 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...
431
				$user_settings['avatar'] = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($user_settings['avatar']) . '&hash=' . md5($user_settings['avatar'] . $image_proxy_secret);
432
433
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
434
				cache_put_data('user_settings-' . $id_member, $user_settings, 60);
435
		}
436
437
		// Did we find 'im?  If not, junk it.
438
		if (!empty($user_settings))
439
		{
440
			// As much as the password should be right, we can assume the integration set things up.
441
			if (!empty($already_verified) && $already_verified === true)
442
				$check = true;
443
			// SHA-512 hash should be 128 characters long.
444
			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...
445
				$check = hash_salt($user_settings['passwd'], $user_settings['password_salt']) == $password;
446
			else
447
				$check = false;
448
449
			// Wrong password or not activated - either way, you're going nowhere.
450
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
451
		}
452
		else
453
			$id_member = 0;
454
455
		// If we no longer have the member maybe they're being all hackey, stop brute force!
456
		if (!$id_member)
457
		{
458
			require_once($sourcedir . '/LogInOut.php');
459
			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);
460
		}
461
		// Validate for Two Factor Authentication
462
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], array('login2', 'logintfa'))))
463
		{
464
			$tfacookie = $cookiename . '_tfa';
465
			$tfasecret = null;
466
467
			$verified = call_integration_hook('integrate_verify_tfa', array($id_member, $user_settings));
468
469
			if (empty($verified) || !in_array(true, $verified))
470
			{
471
				if (!empty($_COOKIE[$tfacookie]))
472
				{
473
					$tfa_data = smf_json_decode($_COOKIE[$tfacookie]);
474
475
					if (is_null($tfa_data))
476
						$tfa_data = safe_unserialize($_COOKIE[$tfacookie]);
477
478
					list ($tfamember, $tfasecret) = $tfa_data;
479
480
					if ((int) $tfamember != $id_member)
481
						$tfasecret = null;
482
				}
483
484
				if (empty($tfasecret) || hash_salt($user_settings['tfa_backup'], $user_settings['password_salt']) != $tfasecret)
485
				{
486
					$id_member = 0;
487
					redirectexit('action=logintfa');
488
				}
489
			}
490
		}
491
		// When authenticating their two factor code, make sure to reset their ID for security
492
		elseif (!empty($modSettings['tfa_mode']) && $id_member && !empty($user_settings['tfa_secret']) && $_REQUEST['action'] == 'logintfa')
493
		{
494
			$id_member = 0;
495
			$context['tfa_member'] = $user_settings;
496
			$user_settings = array();
497
		}
498
		// Are we forcing 2FA? Need to check if the user groups actually require 2FA
499
		elseif (!empty($modSettings['tfa_mode']) && $modSettings['tfa_mode'] >= 2 && $id_member && empty($user_settings['tfa_secret']))
500
		{
501
			if ($modSettings['tfa_mode'] == 2) //only do this if we are just forcing SOME membergroups
502
			{
503
				//Build an array of ALL user membergroups.
504
				$full_groups = array($user_settings['id_group']);
505
				if (!empty($user_settings['additional_groups']))
506
				{
507
					$full_groups = array_merge($full_groups, explode(',', $user_settings['additional_groups']));
508
					$full_groups = array_unique($full_groups); //duplicates, maybe?
509
				}
510
511
				//Find out if any group requires 2FA
512
				$request = $smcFunc['db_query']('', '
513
					SELECT COUNT(id_group) AS total
514
					FROM {db_prefix}membergroups
515
					WHERE tfa_required = {int:tfa_required}
516
						AND id_group IN ({array_int:full_groups})',
517
					array(
518
						'tfa_required' => 1,
519
						'full_groups' => $full_groups,
520
					)
521
				);
522
				$row = $smcFunc['db_fetch_assoc']($request);
523
				$smcFunc['db_free_result']($request);
524
			}
525
			else
526
				$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...
527
528
			$area = !empty($_REQUEST['area']) ? $_REQUEST['area'] : '';
529
			$action = !empty($_REQUEST['action']) ? $_REQUEST['action'] : '';
530
531
			if ($row['total'] > 0 && !in_array($action, array('profile', 'logout')) || ($action == 'profile' && $area != 'tfasetup'))
532
				redirectexit('action=profile;area=tfasetup;forced');
533
		}
534
	}
535
536
	// Found 'im, let's set up the variables.
537
	if ($id_member != 0)
538
	{
539
		// Let's not update the last visit time in these cases...
540
		// 1. SSI doesn't count as visiting the forum.
541
		// 2. RSS feeds and XMLHTTP requests don't count either.
542
		// 3. If it was set within this session, no need to set it again.
543
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
544
		// 5. We're still logging in or authenticating
545
		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))
546
		{
547
			// @todo can this be cached?
548
			// Do a quick query to make sure this isn't a mistake.
549
			$result = $smcFunc['db_query']('', '
550
				SELECT poster_time
551
				FROM {db_prefix}messages
552
				WHERE id_msg = {int:id_msg}
553
				LIMIT 1',
554
				array(
555
					'id_msg' => $user_settings['id_msg_last_visit'],
556
				)
557
			);
558
			list ($visitTime) = $smcFunc['db_fetch_row']($result);
559
			$smcFunc['db_free_result']($result);
560
561
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
562
563
			// If it was *at least* five hours ago...
564
			if ($visitTime < time() - 5 * 3600)
565
			{
566
				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']));
567
				$user_settings['last_login'] = time();
568
569
				if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
570
					cache_put_data('user_settings-' . $id_member, $user_settings, 60);
571
572
				if (!empty($modSettings['cache_enable']))
573
					cache_put_data('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
574
			}
575
		}
576
		elseif (empty($_SESSION['id_msg_last_visit']))
577
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
578
579
		$username = $user_settings['member_name'];
580
581
		if (empty($user_settings['additional_groups']))
582
			$user_info = array(
583
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
584
			);
585 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...
586
			$user_info = array(
587
				'groups' => array_merge(
588
					array($user_settings['id_group'], $user_settings['id_post_group']),
589
					explode(',', $user_settings['additional_groups'])
590
				)
591
			);
592
593
		// Because history has proven that it is possible for groups to go bad - clean up in case.
594
		foreach ($user_info['groups'] as $k => $v)
595
			$user_info['groups'][$k] = (int) $v;
596
597
		// This is a logged in user, so definitely not a spider.
598
		$user_info['possibly_robot'] = false;
599
600
		// Figure out the new time offset.
601
		if (!empty($user_settings['timezone']))
602
		{
603
			// Get the offsets from UTC for the server, then for the user.
604
			$tz_system = new DateTimeZone(@date_default_timezone_get());
605
			$tz_user = new DateTimeZone($user_settings['timezone']);
606
			$time_system = new DateTime('now', $tz_system);
607
			$time_user = new DateTime('now', $tz_user);
608
			$user_info['time_offset'] = ($tz_user->getOffset($time_user) - $tz_system->getOffset($time_system)) / 3600;
609
		}
610
		else
611
		{
612
			// !!! Compatibility.
613
			$user_info['time_offset'] = empty($user_settings['time_offset']) ? 0 :$user_settings['time_offset'];
614
		}
615
	}
616
	// If the user is a guest, initialize all the critical user settings.
617
	else
618
	{
619
		// This is what a guest's variables should be.
620
		$username = '';
621
		$user_info = array('groups' => array(-1));
622
		$user_settings = array();
623
624
		if (isset($_COOKIE[$cookiename]) && empty($context['tfa_member']))
625
			$_COOKIE[$cookiename] = '';
626
627
		// Expire the 2FA cookie
628
		if (isset($_COOKIE[$cookiename . '_tfa']) && empty($context['tfa_member']))
629
		{
630
			$tfa_data = smf_json_decode($_COOKIE[$cookiename . '_tfa'], true);
631
632
			if (is_null($tfa_data))
633
				$tfa_data = safe_unserialize($_COOKIE[$cookiename . '_tfa']);
634
635
			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...
636
637
			if (!$preserve || time() > $exp)
638
			{
639
				$_COOKIE[$cookiename . '_tfa'] = '';
640
				setTFACookie(-3600, 0, '');
641
			}
642
		}
643
644
		// Create a login token if it doesn't exist yet.
645
		if (!isset($_SESSION['token']['post-login']))
646
			createToken('login');
647
		else
648
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
649
650
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
651
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
652
		{
653
			require_once($sourcedir . '/ManageSearchEngines.php');
654
			$user_info['possibly_robot'] = SpiderCheck();
655
		}
656
		elseif (!empty($modSettings['spider_mode']))
657
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
658
		// If we haven't turned on proper spider hunts then have a guess!
659
		else
660
		{
661
			$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
662
			$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;
663
		}
664
665
		// We don't know the offset...
666
		$user_info['time_offset'] = 0;
667
	}
668
669
	// Set up the $user_info array.
670
	$user_info += array(
671
		'id' => $id_member,
672
		'username' => $username,
673
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
674
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
675
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
676
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
677
		'is_guest' => $id_member == 0,
678
		'is_admin' => in_array(1, $user_info['groups']),
679
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
680
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
681
		'ip' => $_SERVER['REMOTE_ADDR'],
682
		'ip2' => $_SERVER['BAN_CHECK_IP'],
683
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
684
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
685
		'avatar' => array(
686
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
687
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
688
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
689
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
690
		),
691
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
692
		'messages' => empty($user_settings['instant_messages']) ? 0 : $user_settings['instant_messages'],
693
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
694
		'alerts' => empty($user_settings['alerts']) ? 0 : $user_settings['alerts'],
695
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
696
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
697
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
698
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
699
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
700
		'permissions' => array(),
701
	);
702
	$user_info['groups'] = array_unique($user_info['groups']);
703
704
	// 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.
705
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
706
		unset($user_info['ignoreboards'][$tmp]);
707
708
	// Allow the user to change their language.
709
	if (!empty($modSettings['userLanguage']))
710
	{
711
		$languages = getLanguages();
712
713
		// Is it valid?
714
		if (!empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
715
		{
716
			$user_info['language'] = strtr($_GET['language'], './\\:', '____');
717
718
			// Make it permanent for members.
719
			if (!empty($user_info['id']))
720
				updateMemberData($user_info['id'], array('lngfile' => $user_info['language']));
721
			else
722
				$_SESSION['language'] = $user_info['language'];
723
		}
724
		elseif (!empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
725
			$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
726
	}
727
728
	// Just build this here, it makes it easier to change/use - administrators can see all boards.
729
	if ($user_info['is_admin'])
730
		$user_info['query_see_board'] = '1=1';
731
	// Otherwise just the groups in $user_info['groups'].
732
	else
733
		$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'] : '') . ')';
734
735
	// Build the list of boards they WANT to see.
736
	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
737
738
	// If they aren't ignoring any boards then they want to see all the boards they can see
739
	if (empty($user_info['ignoreboards']))
740
		$user_info['query_wanna_see_board'] = $user_info['query_see_board'];
741
	// Ok I guess they don't want to see all the boards
742
	else
743
		$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
744
745
	call_integration_hook('integrate_user_info');
746
}
747
748
/**
749
 * Check for moderators and see if they have access to the board.
750
 * What it does:
751
 * - sets up the $board_info array for current board information.
752
 * - if cache is enabled, the $board_info array is stored in cache.
753
 * - redirects to appropriate post if only message id is requested.
754
 * - is only used when inside a topic or board.
755
 * - determines the local moderators for the board.
756
 * - adds group id 3 if the user is a local moderator for the board they are in.
757
 * - prevents access if user is not in proper group nor a local moderator of the board.
758
 */
759
function loadBoard()
760
{
761
	global $txt, $scripturl, $context, $modSettings;
762
	global $board_info, $board, $topic, $user_info, $smcFunc;
763
764
	// Assume they are not a moderator.
765
	$user_info['is_mod'] = false;
766
	$context['user']['is_mod'] = &$user_info['is_mod'];
767
768
	// Start the linktree off empty..
769
	$context['linktree'] = array();
770
771
	// Have they by chance specified a message id but nothing else?
772
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
773
	{
774
		// Make sure the message id is really an int.
775
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
776
777
		// Looking through the message table can be slow, so try using the cache first.
778
		if (($topic = cache_get_data('msg_topic-' . $_REQUEST['msg'], 120)) === null)
779
		{
780
			$request = $smcFunc['db_query']('', '
781
				SELECT id_topic
782
				FROM {db_prefix}messages
783
				WHERE id_msg = {int:id_msg}
784
				LIMIT 1',
785
				array(
786
					'id_msg' => $_REQUEST['msg'],
787
				)
788
			);
789
790
			// So did it find anything?
791
			if ($smcFunc['db_num_rows']($request))
792
			{
793
				list ($topic) = $smcFunc['db_fetch_row']($request);
794
				$smcFunc['db_free_result']($request);
795
				// Save save save.
796
				cache_put_data('msg_topic-' . $_REQUEST['msg'], $topic, 120);
797
			}
798
		}
799
800
		// Remember redirection is the key to avoiding fallout from your bosses.
801
		if (!empty($topic))
802
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
803
		else
804
		{
805
			loadPermissions();
806
			loadTheme();
807
			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...
808
		}
809
	}
810
811
	// Load this board only if it is specified.
812
	if (empty($board) && empty($topic))
813
	{
814
		$board_info = array('moderators' => array(), 'moderator_groups' => array());
815
		return;
816
	}
817
818
	if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
819
	{
820
		// @todo SLOW?
821
		if (!empty($topic))
822
			$temp = cache_get_data('topic_board-' . $topic, 120);
823
		else
824
			$temp = cache_get_data('board-' . $board, 120);
825
826
		if (!empty($temp))
827
		{
828
			$board_info = $temp;
829
			$board = $board_info['id'];
830
		}
831
	}
832
833
	if (empty($temp))
834
	{
835
		$request = $smcFunc['db_query']('', '
836
			SELECT
837
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
838
				b.id_parent, c.name AS cname, COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name,
839
				COALESCE(mem.id_member, 0) AS id_moderator,
840
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
841
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
842
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . '
843
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
844
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . '
845
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
846
				LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = {raw:board_link})
847
				LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
848
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
849
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
850
			WHERE b.id_board = {raw:board_link}',
851
			array(
852
				'current_topic' => $topic,
853
				'board_link' => empty($topic) ? $smcFunc['db_quote']('{int:current_board}', array('current_board' => $board)) : 't.id_board',
854
			)
855
		);
856
		// If there aren't any, skip.
857
		if ($smcFunc['db_num_rows']($request) > 0)
858
		{
859
			$row = $smcFunc['db_fetch_assoc']($request);
860
861
			// Set the current board.
862
			if (!empty($row['id_board']))
863
				$board = $row['id_board'];
864
865
			// Basic operating information. (globals... :/)
866
			$board_info = array(
867
				'id' => $board,
868
				'moderators' => array(),
869
				'moderator_groups' => array(),
870
				'cat' => array(
871
					'id' => $row['id_cat'],
872
					'name' => $row['cname']
873
				),
874
				'name' => $row['bname'],
875
				'description' => $row['description'],
876
				'num_topics' => $row['num_topics'],
877
				'unapproved_topics' => $row['unapproved_topics'],
878
				'unapproved_posts' => $row['unapproved_posts'],
879
				'unapproved_user_topics' => 0,
880
				'parent_boards' => getBoardParents($row['id_parent']),
881
				'parent' => $row['id_parent'],
882
				'child_level' => $row['child_level'],
883
				'theme' => $row['id_theme'],
884
				'override_theme' => !empty($row['override_theme']),
885
				'profile' => $row['id_profile'],
886
				'redirect' => $row['redirect'],
887
				'recycle' => !empty($modSettings['recycle_enable']) && !empty($modSettings['recycle_board']) && $modSettings['recycle_board'] == $board,
888
				'posts_count' => empty($row['count_posts']),
889
				'cur_topic_approved' => empty($topic) || $row['approved'],
890
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
891
			);
892
893
			// Load the membergroups allowed, and check permissions.
894
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
895
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
896
897
			do
898
			{
899 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...
900
					$board_info['moderators'][$row['id_moderator']] = array(
901
						'id' => $row['id_moderator'],
902
						'name' => $row['real_name'],
903
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
904
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
905
					);
906
907 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...
908
					$board_info['moderator_groups'][$row['id_moderator_group']] = array(
909
						'id' => $row['id_moderator_group'],
910
						'name' => $row['group_name'],
911
						'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
912
						'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
913
					);
914
			}
915
			while ($row = $smcFunc['db_fetch_assoc']($request));
916
917
			// If the board only contains unapproved posts and the user isn't an approver then they can't see any topics.
918
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
919
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
920
			{
921
				// Free the previous result
922
				$smcFunc['db_free_result']($request);
923
924
				// @todo why is this using id_topic?
925
				// @todo Can this get cached?
926
				$request = $smcFunc['db_query']('', '
927
					SELECT COUNT(id_topic)
928
					FROM {db_prefix}topics
929
					WHERE id_member_started={int:id_member}
930
						AND approved = {int:unapproved}
931
						AND id_board = {int:board}',
932
					array(
933
						'id_member' => $user_info['id'],
934
						'unapproved' => 0,
935
						'board' => $board,
936
					)
937
				);
938
939
				list ($board_info['unapproved_user_topics']) = $smcFunc['db_fetch_row']($request);
940
			}
941
942
			if (!empty($modSettings['cache_enable']) && (empty($topic) || $modSettings['cache_enable'] >= 3))
943
			{
944
				// @todo SLOW?
945
				if (!empty($topic))
946
					cache_put_data('topic_board-' . $topic, $board_info, 120);
947
				cache_put_data('board-' . $board, $board_info, 120);
948
			}
949
		}
950
		else
951
		{
952
			// Otherwise the topic is invalid, there are no moderators, etc.
953
			$board_info = array(
954
				'moderators' => array(),
955
				'moderator_groups' => array(),
956
				'error' => 'exist'
957
			);
958
			$topic = null;
959
			$board = 0;
960
		}
961
		$smcFunc['db_free_result']($request);
962
	}
963
964
	if (!empty($topic))
965
		$_GET['board'] = (int) $board;
966
967
	if (!empty($board))
968
	{
969
		// Get this into an array of keys for array_intersect
970
		$moderator_groups = array_keys($board_info['moderator_groups']);
971
972
		// Now check if the user is a moderator.
973
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]) || count(array_intersect($user_info['groups'], $moderator_groups)) != 0;
974
975 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...
976
			$board_info['error'] = 'access';
977 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...
978
			$board_info['error'] = 'access';
979
980
		// Build up the linktree.
981
		$context['linktree'] = array_merge(
982
			$context['linktree'],
983
			array(array(
984
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
985
				'name' => $board_info['cat']['name']
986
			)),
987
			array_reverse($board_info['parent_boards']),
988
			array(array(
989
				'url' => $scripturl . '?board=' . $board . '.0',
990
				'name' => $board_info['name']
991
			))
992
		);
993
	}
994
995
	// Set the template contextual information.
996
	$context['user']['is_mod'] = &$user_info['is_mod'];
997
	$context['current_topic'] = $topic;
998
	$context['current_board'] = $board;
999
1000
	// No posting in redirection boards!
1001
	if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'post' && !empty($board_info['redirect']))
1002
		$board_info['error'] == 'post_in_redirect';
1003
1004
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
1005
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_mod']))
1006
	{
1007
		// The permissions and theme need loading, just to make sure everything goes smoothly.
1008
		loadPermissions();
1009
		loadTheme();
1010
1011
		$_GET['board'] = '';
1012
		$_GET['topic'] = '';
1013
1014
		// The linktree should not give the game away mate!
1015
		$context['linktree'] = array(
1016
			array(
1017
				'url' => $scripturl,
1018
				'name' => $context['forum_name_html_safe']
1019
			)
1020
		);
1021
1022
		// If it's a prefetching agent or we're requesting an attachment.
1023
		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...
1024
		{
1025
			ob_end_clean();
1026
			header('HTTP/1.1 403 Forbidden');
1027
			die;
1028
		}
1029
		elseif ($board_info['error'] == 'post_in_redirect')
1030
		{
1031
			// Slightly different error message here...
1032
			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...
1033
		}
1034
		elseif ($user_info['is_guest'])
1035
		{
1036
			loadLanguage('Errors');
1037
			is_not_guest($txt['topic_gone']);
1038
		}
1039
		else
1040
			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...
1041
	}
1042
1043
	if ($user_info['is_mod'])
1044
		$user_info['groups'][] = 3;
1045
}
1046
1047
/**
1048
 * Load this user's permissions.
1049
 */
1050
function loadPermissions()
1051
{
1052
	global $user_info, $board, $board_info, $modSettings, $smcFunc, $sourcedir;
1053
1054
	if ($user_info['is_admin'])
1055
	{
1056
		banPermissions();
1057
		return;
1058
	}
1059
1060
	if (!empty($modSettings['cache_enable']))
1061
	{
1062
		$cache_groups = $user_info['groups'];
1063
		asort($cache_groups);
1064
		$cache_groups = implode(',', $cache_groups);
1065
		// If it's a spider then cache it different.
1066
		if ($user_info['possibly_robot'])
1067
			$cache_groups .= '-spider';
1068
1069
		if ($modSettings['cache_enable'] >= 2 && !empty($board) && ($temp = cache_get_data('permissions:' . $cache_groups . ':' . $board, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1070
		{
1071
			list ($user_info['permissions']) = $temp;
1072
			banPermissions();
1073
1074
			return;
1075
		}
1076
		elseif (($temp = cache_get_data('permissions:' . $cache_groups, 240)) != null && time() - 240 > $modSettings['settings_updated'])
1077
			list ($user_info['permissions'], $removals) = $temp;
1078
	}
1079
1080
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
1081
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
1082
1083
	if (empty($user_info['permissions']))
1084
	{
1085
		// Get the general permissions.
1086
		$request = $smcFunc['db_query']('', '
1087
			SELECT permission, add_deny
1088
			FROM {db_prefix}permissions
1089
			WHERE id_group IN ({array_int:member_groups})
1090
				' . $spider_restrict,
1091
			array(
1092
				'member_groups' => $user_info['groups'],
1093
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1094
			)
1095
		);
1096
		$removals = array();
1097 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...
1098
		{
1099
			if (empty($row['add_deny']))
1100
				$removals[] = $row['permission'];
1101
			else
1102
				$user_info['permissions'][] = $row['permission'];
1103
		}
1104
		$smcFunc['db_free_result']($request);
1105
1106
		if (isset($cache_groups))
1107
			cache_put_data('permissions:' . $cache_groups, array($user_info['permissions'], $removals), 240);
1108
	}
1109
1110
	// Get the board permissions.
1111
	if (!empty($board))
1112
	{
1113
		// Make sure the board (if any) has been loaded by loadBoard().
1114
		if (!isset($board_info['profile']))
1115
			fatal_lang_error('no_board');
1116
1117
		$request = $smcFunc['db_query']('', '
1118
			SELECT permission, add_deny
1119
			FROM {db_prefix}board_permissions
1120
			WHERE (id_group IN ({array_int:member_groups})
1121
				' . $spider_restrict . ')
1122
				AND id_profile = {int:id_profile}',
1123
			array(
1124
				'member_groups' => $user_info['groups'],
1125
				'id_profile' => $board_info['profile'],
1126
				'spider_group' => !empty($modSettings['spider_group']) ? $modSettings['spider_group'] : 0,
1127
			)
1128
		);
1129 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...
1130
		{
1131
			if (empty($row['add_deny']))
1132
				$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...
1133
			else
1134
				$user_info['permissions'][] = $row['permission'];
1135
		}
1136
		$smcFunc['db_free_result']($request);
1137
	}
1138
1139
	// Remove all the permissions they shouldn't have ;).
1140
	if (!empty($modSettings['permission_enable_deny']))
1141
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
1142
1143
	if (isset($cache_groups) && !empty($board) && $modSettings['cache_enable'] >= 2)
1144
		cache_put_data('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
1145
1146
	// Banned?  Watch, don't touch..
1147
	banPermissions();
1148
1149
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
1150
	if (!$user_info['is_guest'])
1151
	{
1152
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
1153
		{
1154
			require_once($sourcedir . '/Subs-Auth.php');
1155
			rebuildModCache();
1156
		}
1157
		else
1158
			$user_info['mod_cache'] = $_SESSION['mc'];
1159
1160
		// This is a useful phantom permission added to the current user, and only the current user while they are logged in.
1161
		// For example this drastically simplifies certain changes to the profile area.
1162
		$user_info['permissions'][] = 'is_not_guest';
1163
		// And now some backwards compatibility stuff for mods and whatnot that aren't expecting the new permissions.
1164
		$user_info['permissions'][] = 'profile_view_own';
1165
		if (in_array('profile_view', $user_info['permissions']))
1166
			$user_info['permissions'][] = 'profile_view_any';
1167
	}
1168
}
1169
1170
/**
1171
 * Loads an array of users' data by ID or member_name.
1172
 *
1173
 * @param array|string $users An array of users by id or name or a single username/id
1174
 * @param bool $is_name Whether $users contains names
1175
 * @param string $set What kind of data to load (normal, profile, minimal)
1176
 * @return array The ids of the members loaded
1177
 */
1178
function loadMemberData($users, $is_name = false, $set = 'normal')
1179
{
1180
	global $user_profile, $modSettings, $board_info, $smcFunc, $context;
1181
	global $image_proxy_enabled, $image_proxy_secret, $boardurl;
1182
1183
	// Can't just look for no users :P.
1184
	if (empty($users))
1185
		return array();
1186
1187
	// Pass the set value
1188
	$context['loadMemberContext_set'] = $set;
1189
1190
	// Make sure it's an array.
1191
	$users = !is_array($users) ? array($users) : array_unique($users);
1192
	$loaded_ids = array();
1193
1194
	if (!$is_name && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1195
	{
1196
		$users = array_values($users);
1197
		for ($i = 0, $n = count($users); $i < $n; $i++)
1198
		{
1199
			$data = cache_get_data('member_data-' . $set . '-' . $users[$i], 240);
1200
			if ($data == null)
1201
				continue;
1202
1203
			$loaded_ids[] = $data['id_member'];
1204
			$user_profile[$data['id_member']] = $data;
1205
			unset($users[$i]);
1206
		}
1207
	}
1208
1209
	// Used by default
1210
	$select_columns = '
1211
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
1212
			mem.signature, mem.personal_text, mem.avatar, mem.id_member, mem.member_name,
1213
			mem.real_name, mem.email_address, mem.date_registered, mem.website_title, mem.website_url,
1214
			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,
1215
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
1216
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
1217
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
1218
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
1219
	$select_tables = '
1220
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
1221
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
1222
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1223
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
1224
1225
	// We add or replace according the the set
1226
	switch ($set)
1227
	{
1228
		case 'normal':
1229
			$select_columns .= ', mem.buddy_list,  mem.additional_groups';
1230
			break;
1231
		case 'profile':
1232
			$select_columns .= ', mem.additional_groups, mem.id_theme, mem.pm_ignore_list, mem.pm_receive_from,
1233
			mem.time_format, mem.timezone, mem.secret_question, mem.smiley_set, mem.tfa_secret,
1234
			mem.total_time_logged_in, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.alerts';
1235
			break;
1236
		case 'minimal':
1237
			$select_columns = '
1238
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.date_registered,
1239
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
1240
			$select_tables = '';
1241
			break;
1242
		default:
1243
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
1244
	}
1245
1246
	// Allow mods to easily add to the selected member data
1247
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, &$set));
1248
1249
	if (!empty($users))
1250
	{
1251
		// Load the member's data.
1252
		$request = $smcFunc['db_query']('', '
1253
			SELECT' . $select_columns . '
1254
			FROM {db_prefix}members AS mem' . $select_tables . '
1255
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})',
1256
			array(
1257
				'blank_string' => '',
1258
				'users' => $users,
1259
			)
1260
		);
1261
		$new_loaded_ids = array();
1262
		while ($row = $smcFunc['db_fetch_assoc']($request))
1263
		{
1264
			// Take care of proxying avatar if required, do this here for maximum reach
1265 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...
1266
				$row['avatar'] = $boardurl . '/proxy.php?request=' . urlencode($row['avatar']) . '&hash=' . md5($row['avatar'] . $image_proxy_secret);
1267
1268
			if ( isset($row['member_ip']) )
1269
				$row['member_ip'] = inet_dtop($row['member_ip']);
1270
			if ( isset($row['member_ip2']) )
1271
				$row['member_ip2'] = inet_dtop($row['member_ip2']);
1272
			$new_loaded_ids[] = $row['id_member'];
1273
			$loaded_ids[] = $row['id_member'];
1274
			$row['options'] = array();
1275
			$user_profile[$row['id_member']] = $row;
1276
		}
1277
		$smcFunc['db_free_result']($request);
1278
	}
1279
1280 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...
1281
	{
1282
		$request = $smcFunc['db_query']('', '
1283
			SELECT id_member, variable, value
1284
			FROM {db_prefix}themes
1285
			WHERE id_member IN ({array_int:loaded_ids})',
1286
			array(
1287
				'loaded_ids' => $new_loaded_ids,
1288
			)
1289
		);
1290
		while ($row = $smcFunc['db_fetch_assoc']($request))
1291
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
1292
		$smcFunc['db_free_result']($request);
1293
	}
1294
1295
	$additional_mods = array();
1296
1297
	// Are any of these users in groups assigned to moderate this board?
1298
	if (!empty($loaded_ids) && !empty($board_info['moderator_groups']) && $set === 'normal')
1299
	{
1300
		foreach ($loaded_ids as $a_member)
1301
		{
1302
			if (!empty($user_profile[$a_member]['additional_groups']))
1303
				$groups = array_merge(array($user_profile[$a_member]['id_group']), explode(',', $user_profile[$a_member]['additional_groups']));
1304
			else
1305
				$groups = array($user_profile[$a_member]['id_group']);
1306
1307
			$temp = array_intersect($groups, array_keys($board_info['moderator_groups']));
1308
1309
			if (!empty($temp))
1310
			{
1311
				$additional_mods[] = $a_member;
1312
			}
1313
		}
1314
	}
1315
1316
	if (!empty($new_loaded_ids) && !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 3)
1317
	{
1318
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
1319
			cache_put_data('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
1320
	}
1321
1322
	// Are we loading any moderators?  If so, fix their group data...
1323
	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)
1324
	{
1325 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...
1326
		{
1327
			$request = $smcFunc['db_query']('', '
1328
				SELECT group_name AS member_group, online_color AS member_group_color, icons
1329
				FROM {db_prefix}membergroups
1330
				WHERE id_group = {int:moderator_group}
1331
				LIMIT 1',
1332
				array(
1333
					'moderator_group' => 3,
1334
				)
1335
			);
1336
			$row = $smcFunc['db_fetch_assoc']($request);
1337
			$smcFunc['db_free_result']($request);
1338
1339
			cache_put_data('moderator_group_info', $row, 480);
1340
		}
1341
1342
		foreach ($temp_mods as $id)
1343
		{
1344
			// By popular demand, don't show admins or global moderators as moderators.
1345
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
1346
				$user_profile[$id]['member_group'] = $row['member_group'];
1347
1348
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1349
			if (!empty($row['icons']))
1350
				$user_profile[$id]['icons'] = $row['icons'];
1351
			if (!empty($row['member_group_color']))
1352
				$user_profile[$id]['member_group_color'] = $row['member_group_color'];
1353
		}
1354
	}
1355
1356
	return $loaded_ids;
1357
}
1358
1359
/**
1360
 * Loads the user's basic values... meant for template/theme usage.
1361
 *
1362
 * @param int $user The ID of a user previously loaded by {@link loadMemberData()}
1363
 * @param bool $display_custom_fields Whether or not to display custom profile fields
1364
 * @return boolean Whether or not the data was loaded successfully
1365
 */
1366
function loadMemberContext($user, $display_custom_fields = false)
1367
{
1368
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1369
	global $context, $modSettings, $settings, $smcFunc;
1370
	static $dataLoaded = array();
1371
	static $loadedLanguages = array();
1372
1373
	// If this person's data is already loaded, skip it.
1374
	if (isset($dataLoaded[$user]))
1375
		return true;
1376
1377
	// We can't load guests or members not loaded by loadMemberData()!
1378
	if ($user == 0)
1379
		return false;
1380
	if (!isset($user_profile[$user]))
1381
	{
1382
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1383
		return false;
1384
	}
1385
1386
	// Well, it's loaded now anyhow.
1387
	$dataLoaded[$user] = true;
1388
	$profile = $user_profile[$user];
1389
1390
	// Censor everything.
1391
	censorText($profile['signature']);
1392
	censorText($profile['personal_text']);
1393
1394
	// Set things up to be used before hand.
1395
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br>', ''), $profile['signature']);
1396
	$profile['signature'] = parse_bbc($profile['signature'], true, 'sig' . $profile['id_member']);
1397
1398
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1399
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1400
	// Setup the buddy status here (One whole in_array call saved :P)
1401
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1402
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1403
1404
	//We need a little fallback for the membergroup icons. If it doesn't exist in the current theme, fallback to default theme
1405
	if (isset($profile['icons'][1]) && file_exists($settings['actual_theme_dir'] . '/images/membericons/' . $profile['icons'][1])) //icon is set and exists
1406
		$group_icon_url = $settings['images_url'] . '/membericons/' . $profile['icons'][1];
1407
	elseif (isset($profile['icons'][1])) //icon is set and doesn't exist, fallback to default
1408
		$group_icon_url = $settings['default_images_url'] . '/membericons/' . $profile['icons'][1];
1409
	else //not set, bye bye
1410
		$group_icon_url = '';
1411
1412
	// These minimal values are always loaded
1413
	$memberContext[$user] = array(
1414
		'username' => $profile['member_name'],
1415
		'name' => $profile['real_name'],
1416
		'id' => $profile['id_member'],
1417
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1418
		'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>',
1419
		'email' => $profile['email_address'],
1420
		'show_email' => !$user_info['is_guest'] && ($user_info['id'] == $profile['id_member'] || allowedTo('moderate_forum')),
1421
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : timeformat($profile['date_registered']),
1422
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1423
	);
1424
1425
	// If the set isn't minimal then load the monstrous array.
1426
	if ($context['loadMemberContext_set'] != 'minimal')
1427
	{
1428
		// Go the extra mile and load the user's native language name.
1429
		if (empty($loadedLanguages))
1430
			$loadedLanguages = getLanguages();
1431
1432
		$memberContext[$user] += array(
1433
			'username_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['member_name'] .'</span>',
1434
			'name_color' => '<span '. (!empty($profile['member_group_color']) ? 'style="color:'. $profile['member_group_color'] .';"' : '') .'>'. $profile['real_name'] .'</span>',
1435
			'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>',
1436
			'is_buddy' => $profile['buddy'],
1437
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1438
			'buddies' => $buddy_list,
1439
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1440
			'blurb' => $profile['personal_text'],
1441
			'website' => array(
1442
				'title' => $profile['website_title'],
1443
				'url' => $profile['website_url'],
1444
			),
1445
			'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']),
1446
			'signature' => $profile['signature'],
1447
			'real_posts' => $profile['posts'],
1448
			'posts' => $profile['posts'] > 500000 ? $txt['geek'] : comma_format($profile['posts']),
1449
			'last_login' => empty($profile['last_login']) ? $txt['never'] : timeformat($profile['last_login']),
1450
			'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...
1451
			'ip' => $smcFunc['htmlspecialchars']($profile['member_ip']),
1452
			'ip2' => $smcFunc['htmlspecialchars']($profile['member_ip2']),
1453
			'online' => array(
1454
				'is_online' => $profile['is_online'],
1455
				'text' => $smcFunc['htmlspecialchars']($txt[$profile['is_online'] ? 'online' : 'offline']),
1456
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], $smcFunc['htmlspecialchars']($profile['real_name'])),
1457
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1458
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1459
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1460
			),
1461
			'language' => !empty($loadedLanguages[$profile['lngfile']]) && !empty($loadedLanguages[$profile['lngfile']]['name']) ? $loadedLanguages[$profile['lngfile']]['name'] : $smcFunc['ucwords'](strtr($profile['lngfile'], array('_' => ' ', '-utf8' => ''))),
1462
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1463
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1464
			'options' => $profile['options'],
1465
			'is_guest' => false,
1466
			'group' => $profile['member_group'],
1467
			'group_color' => $profile['member_group_color'],
1468
			'group_id' => $profile['id_group'],
1469
			'post_group' => $profile['post_group'],
1470
			'post_group_color' => $profile['post_group_color'],
1471
			'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]),
1472
			'warning' => $profile['warning'],
1473
			'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' : (''))),
1474
			'local_time' => timeformat(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1475
			'custom_fields' => array(),
1476
		);
1477
	}
1478
1479
	// If the set isn't minimal then load their avatar as well.
1480
	if ($context['loadMemberContext_set'] != 'minimal')
1481
	{
1482
		if (!empty($modSettings['gravatarOverride']) || (!empty($modSettings['gravatarEnabled']) && stristr($profile['avatar'], 'gravatar://')))
1483
		{
1484
			if (!empty($modSettings['gravatarAllowExtraEmail']) && stristr($profile['avatar'], 'gravatar://') && strlen($profile['avatar']) > 11)
1485
				$image = get_gravatar_url($smcFunc['substr']($profile['avatar'], 11));
1486
			else
1487
				$image = get_gravatar_url($profile['email_address']);
1488
		}
1489
		else
1490
		{
1491
			// So it's stored in the member table?
1492
			if (!empty($profile['avatar']))
1493
			{
1494
				$image = (stristr($profile['avatar'], 'http://') || stristr($profile['avatar'], 'https://')) ? $profile['avatar'] : $modSettings['avatar_url'] . '/' . $profile['avatar'];
1495
			}
1496
			elseif (!empty($profile['filename']))
1497
				$image = $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1498
			// Right... no avatar...use the default one
1499
			else
1500
				$image = $modSettings['avatar_url'] . '/default.png';
1501
		}
1502
		if (!empty($image))
1503
			$memberContext[$user]['avatar'] = array(
1504
				'name' => $profile['avatar'],
1505
				'image' => '<img class="avatar" src="' . $image . '" alt="avatar_'. $profile['member_name'].'">',
1506
				'href' => $image,
1507
				'url' => $image,
1508
			);
1509
	}
1510
1511
	// Are we also loading the members custom fields into context?
1512
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1513
	{
1514
		$memberContext[$user]['custom_fields'] = array();
1515
1516
		if (!isset($context['display_fields']))
1517
			$context['display_fields'] = smf_json_decode($modSettings['displayFields'], true);
1518
1519
		foreach ($context['display_fields'] as $custom)
1520
		{
1521
			if (!isset($custom['col_name']) || trim($custom['col_name']) == '' || empty($profile['options'][$custom['col_name']]))
1522
				continue;
1523
1524
			$value = $profile['options'][$custom['col_name']];
1525
1526
			// Don't show the "disabled" option for the "gender" field.
1527
			if ($custom['col_name'] == 'cust_gender' && $value == 'Disabled')
1528
				continue;
1529
1530
			// BBC?
1531
			if ($custom['bbc'])
1532
				$value = parse_bbc($value);
1533
			// ... or checkbox?
1534
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1535
				$value = $value ? $txt['yes'] : $txt['no'];
1536
1537
			// Enclosing the user input within some other text?
1538 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...
1539
				$value = strtr($custom['enclose'], array(
1540
					'{SCRIPTURL}' => $scripturl,
1541
					'{IMAGES_URL}' => $settings['images_url'],
1542
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1543
					'{INPUT}' => $value,
1544
				));
1545
1546
			$memberContext[$user]['custom_fields'][] = array(
1547
				'title' => !empty($custom['title']) ? $custom['title'] : $custom['col_name'],
1548
				'col_name' => $custom['col_name'],
1549
				'value' => un_htmlspecialchars($value),
1550
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1551
			);
1552
		}
1553
	}
1554
1555
	call_integration_hook('integrate_member_context', array(&$memberContext[$user], $user, $display_custom_fields));
1556
	return true;
1557
}
1558
1559
/**
1560
 * Loads the user's custom profile fields
1561
 *
1562
 * @param integer|array $users A single user ID or an array of user IDs
1563
 * @param string|array $params Either a string or an array of strings with profile field names
1564
 * @return array|boolean An array of data about the fields and their values or false if nothing was loaded
1565
 */
1566
function loadMemberCustomFields($users, $params)
1567
{
1568
	global $smcFunc, $txt, $scripturl, $settings;
1569
1570
	// Do not waste my time...
1571
	if (empty($users) || empty($params))
1572
		return false;
1573
1574
	// Make sure it's an array.
1575
	$users = !is_array($users) ? array($users) : array_unique($users);
1576
	$params = !is_array($params) ? array($params) : array_unique($params);
1577
	$return = array();
1578
1579
	$request = $smcFunc['db_query']('', '
1580
		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,
1581
		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
1582
		FROM {db_prefix}themes AS t
1583
			LEFT JOIN {db_prefix}custom_fields AS c ON (c.col_name = t.variable)
1584
		WHERE id_member IN ({array_int:loaded_ids})
1585
			AND variable IN ({array_string:params})
1586
		ORDER BY field_order',
1587
		array(
1588
			'loaded_ids' => $users,
1589
			'params' => $params,
1590
		)
1591
	);
1592
1593
	while ($row = $smcFunc['db_fetch_assoc']($request))
1594
	{
1595
		// BBC?
1596
		if (!empty($row['bbc']))
1597
			$row['value'] = parse_bbc($row['value']);
1598
1599
		// ... or checkbox?
1600
		elseif (isset($row['type']) && $row['type'] == 'check')
1601
			$row['value'] = !empty($row['value']) ? $txt['yes'] : $txt['no'];
1602
1603
		// Enclosing the user input within some other text?
1604
		if (!empty($row['enclose']))
1605
			$row['value'] = strtr($row['enclose'], array(
1606
				'{SCRIPTURL}' => $scripturl,
1607
				'{IMAGES_URL}' => $settings['images_url'],
1608
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1609
				'{INPUT}' => un_htmlspecialchars($row['value']),
1610
			));
1611
1612
		// Send a simple array if there is just 1 param
1613
		if (count($params) == 1)
1614
			$return[$row['id_member']] = $row;
1615
1616
		// More than 1? knock yourself out...
1617
		else
1618
		{
1619
			if (!isset($return[$row['id_member']]))
1620
				$return[$row['id_member']] = array();
1621
1622
			$return[$row['id_member']][$row['variable']] = $row;
1623
		}
1624
	}
1625
1626
	$smcFunc['db_free_result']($request);
1627
1628
	return !empty($return) ? $return : false;
1629
}
1630
1631
/**
1632
 * Loads information about what browser the user is viewing with and places it in $context
1633
 *  - uses the class from {@link Class-BrowserDetect.php}
1634
 */
1635
function detectBrowser()
1636
{
1637
	// Load the current user's browser of choice
1638
	$detector = new browser_detector;
1639
	$detector->detectBrowser();
1640
}
1641
1642
/**
1643
 * Are we using this browser?
1644
 *
1645
 * Wrapper function for detectBrowser
1646
 * @param string $browser The browser we are checking for.
1647
 * @return bool Whether or not the current browser is what we're looking for
1648
*/
1649
function isBrowser($browser)
1650
{
1651
	global $context;
1652
1653
	// Don't know any browser!
1654
	if (empty($context['browser']))
1655
		detectBrowser();
1656
1657
	return !empty($context['browser'][$browser]) || !empty($context['browser']['is_' . $browser]) ? true : false;
1658
}
1659
1660
/**
1661
 * Load a theme, by ID.
1662
 *
1663
 * @param int $id_theme The ID of the theme to load
1664
 * @param bool $initialize Whether or not to initialize a bunch of theme-related variables/settings
1665
 */
1666
function loadTheme($id_theme = 0, $initialize = true)
1667
{
1668
	global $user_info, $user_settings, $board_info, $boarddir, $maintenance;
1669
	global $txt, $boardurl, $scripturl, $mbname, $modSettings;
1670
	global $context, $settings, $options, $sourcedir, $ssi_theme, $smcFunc, $language, $board, $image_proxy_enabled;
1671
1672
	// The theme was specified by parameter.
1673
	if (!empty($id_theme))
1674
		$id_theme = (int) $id_theme;
1675
	// The theme was specified by REQUEST.
1676 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...
1677
	{
1678
		$id_theme = (int) $_REQUEST['theme'];
1679
		$_SESSION['id_theme'] = $id_theme;
1680
	}
1681
	// The theme was specified by REQUEST... previously.
1682 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...
1683
		$id_theme = (int) $_SESSION['id_theme'];
1684
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1685
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']))
1686
		$id_theme = $user_info['theme'];
1687
	// The theme was specified by the board.
1688
	elseif (!empty($board_info['theme']))
1689
		$id_theme = $board_info['theme'];
1690
	// The theme is the forum's default.
1691
	else
1692
		$id_theme = $modSettings['theme_guests'];
1693
1694
	// Verify the id_theme... no foul play.
1695
	// Always allow the board specific theme, if they are overriding.
1696
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1697
		$id_theme = $board_info['theme'];
1698
	// If they have specified a particular theme to use with SSI allow it to be used.
1699
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1700
		$id_theme = (int) $id_theme;
1701
	elseif (!empty($modSettings['enableThemes']) && !allowedTo('admin_forum'))
1702
	{
1703
		$themes = explode(',', $modSettings['enableThemes']);
1704
		if (!in_array($id_theme, $themes))
1705
			$id_theme = $modSettings['theme_guests'];
1706
		else
1707
			$id_theme = (int) $id_theme;
1708
	}
1709
	else
1710
		$id_theme = (int) $id_theme;
1711
1712
	$member = empty($user_info['id']) ? -1 : $user_info['id'];
1713
1714
	// Disable image proxy if we don't have SSL enabled
1715
	if (empty($modSettings['force_ssl']) || $modSettings['force_ssl'] < 2)
1716
		$image_proxy_enabled = false;
1717
1718
	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'])
1719
	{
1720
		$themeData = $temp;
1721
		$flag = true;
1722
	}
1723
	elseif (($temp = cache_get_data('theme_settings-' . $id_theme, 90)) != null && time() - 60 > $modSettings['settings_updated'])
1724
		$themeData = $temp + array($member => array());
1725
	else
1726
		$themeData = array(-1 => array(), 0 => array(), $member => array());
1727
1728
	if (empty($flag))
1729
	{
1730
		// Load variables from the current or default theme, global or this user's.
1731
		$result = $smcFunc['db_query']('', '
1732
			SELECT variable, value, id_member, id_theme
1733
			FROM {db_prefix}themes
1734
			WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1735
				AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1736
			array(
1737
				'id_theme' => $id_theme,
1738
				'id_member' => $member,
1739
			)
1740
		);
1741
		// Pick between $settings and $options depending on whose data it is.
1742
		while ($row = $smcFunc['db_fetch_assoc']($result))
1743
		{
1744
			// There are just things we shouldn't be able to change as members.
1745
			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')))
1746
				continue;
1747
1748
			// If this is the theme_dir of the default theme, store it.
1749
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1750
				$themeData[0]['default_' . $row['variable']] = $row['value'];
1751
1752
			// If this isn't set yet, is a theme option, or is not the default theme..
1753
			if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1754
				$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1755
		}
1756
		$smcFunc['db_free_result']($result);
1757
1758
		if (!empty($themeData[-1]))
1759
			foreach ($themeData[-1] as $k => $v)
1760
			{
1761
				if (!isset($themeData[$member][$k]))
1762
					$themeData[$member][$k] = $v;
1763
			}
1764
1765
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
1766
			cache_put_data('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1767
		// Only if we didn't already load that part of the cache...
1768
		elseif (!isset($temp))
1769
			cache_put_data('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1770
	}
1771
1772
	$settings = $themeData[0];
1773
	$options = $themeData[$member];
1774
1775
	$settings['theme_id'] = $id_theme;
1776
1777
	$settings['actual_theme_url'] = $settings['theme_url'];
1778
	$settings['actual_images_url'] = $settings['images_url'];
1779
	$settings['actual_theme_dir'] = $settings['theme_dir'];
1780
1781
	$settings['template_dirs'] = array();
1782
	// This theme first.
1783
	$settings['template_dirs'][] = $settings['theme_dir'];
1784
1785
	// Based on theme (if there is one).
1786
	if (!empty($settings['base_theme_dir']))
1787
		$settings['template_dirs'][] = $settings['base_theme_dir'];
1788
1789
	// Lastly the default theme.
1790 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...
1791
		$settings['template_dirs'][] = $settings['default_theme_dir'];
1792
1793
	if (!$initialize)
1794
		return;
1795
1796
	// Check to see if we're forcing SSL
1797
	if (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] == 2 && empty($maintenance) &&
1798
		(!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') && SMF != 'SSI')
1799
		redirectexit(strtr($_SERVER['REQUEST_URL'], array('http://' => 'https://')));
1800
1801
	// Check to see if they're accessing it from the wrong place.
1802
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1803
	{
1804
		$detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1805
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1806
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1807
		if ($temp != '/')
1808
			$detected_url .= $temp;
1809
	}
1810
	if (isset($detected_url) && $detected_url != $boardurl)
1811
	{
1812
		// Try #1 - check if it's in a list of alias addresses.
1813
		if (!empty($modSettings['forum_alias_urls']))
1814
		{
1815
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1816
1817
			foreach ($aliases as $alias)
1818
			{
1819
				// Rip off all the boring parts, spaces, etc.
1820
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1821
					$do_fix = true;
1822
			}
1823
		}
1824
1825
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1826
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && SMF != 'SSI')
1827
		{
1828
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1829
			if (empty($_GET))
1830
				redirectexit('wwwRedirect');
1831
			else
1832
			{
1833
				list ($k, $v) = each($_GET);
1834
1835
				if ($k != 'wwwRedirect')
1836
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1837
			}
1838
		}
1839
1840
		// #3 is just a check for SSL...
1841
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1842
			$do_fix = true;
1843
1844
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1845
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1846
		{
1847
			// Caching is good ;).
1848
			$oldurl = $boardurl;
1849
1850
			// Fix $boardurl and $scripturl.
1851
			$boardurl = $detected_url;
1852
			$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1853
			$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1854
1855
			// Fix the theme urls...
1856
			$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1857
			$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1858
			$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1859
			$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1860
			$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1861
			$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1862
1863
			// And just a few mod settings :).
1864
			$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1865
			$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1866
1867
			// Clean up after loadBoard().
1868
			if (isset($board_info['moderators']))
1869
			{
1870
				foreach ($board_info['moderators'] as $k => $dummy)
1871
				{
1872
					$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1873
					$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1874
				}
1875
			}
1876
			foreach ($context['linktree'] as $k => $dummy)
1877
				$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1878
		}
1879
	}
1880
	// Set up the contextual user array.
1881
	if (!empty($user_info))
1882
	{
1883
		$context['user'] = array(
1884
			'id' => $user_info['id'],
1885
			'is_logged' => !$user_info['is_guest'],
1886
			'is_guest' => &$user_info['is_guest'],
1887
			'is_admin' => &$user_info['is_admin'],
1888
			'is_mod' => &$user_info['is_mod'],
1889
			// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1890
			'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'])))),
1891
			'username' => $user_info['username'],
1892
			'language' => $user_info['language'],
1893
			'email' => $user_info['email'],
1894
			'ignoreusers' => $user_info['ignoreusers'],
1895
		);
1896
		if (!$context['user']['is_guest'])
1897
			$context['user']['name'] = $user_info['name'];
1898 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...
1899
			$context['user']['name'] = $txt['guest_title'];
1900
1901
		// Determine the current smiley set.
1902
		$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'];
1903
		$context['user']['smiley_set'] = $user_info['smiley_set'];
1904
	}
1905
	else
1906
	{
1907
		$context['user'] = array(
1908
			'id' => -1,
1909
			'is_logged' => false,
1910
			'is_guest' => true,
1911
			'is_mod' => false,
1912
			'can_mod' => false,
1913
			'name' => $txt['guest_title'],
1914
			'language' => $language,
1915
			'email' => '',
1916
			'ignoreusers' => array(),
1917
		);
1918
	}
1919
1920
	// Some basic information...
1921
	if (!isset($context['html_headers']))
1922
		$context['html_headers'] = '';
1923
	if (!isset($context['javascript_files']))
1924
		$context['javascript_files'] = array();
1925
	if (!isset($context['css_files']))
1926
		$context['css_files'] = array();
1927
	if (!isset($context['css_header']))
1928
		$context['css_header'] = array();
1929
	if (!isset($context['javascript_inline']))
1930
		$context['javascript_inline'] = array('standard' => array(), 'defer' => array());
1931
	if (!isset($context['javascript_vars']))
1932
		$context['javascript_vars'] = array();
1933
1934
	$context['login_url'] = (!empty($modSettings['force_ssl']) && $modSettings['force_ssl'] < 2 ? strtr($scripturl, array('http://' => 'https://')) : $scripturl) . '?action=login2';
1935
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1936
	$context['session_var'] = $_SESSION['session_var'];
1937
	$context['session_id'] = $_SESSION['session_value'];
1938
	$context['forum_name'] = $mbname;
1939
	$context['forum_name_html_safe'] = $smcFunc['htmlspecialchars']($context['forum_name']);
1940
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? '' : $smcFunc['htmlspecialchars']($settings['header_logo_url']);
1941
	$context['current_action'] = isset($_REQUEST['action']) ? $smcFunc['htmlspecialchars']($_REQUEST['action']) : null;
1942
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1943
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
1944
	if (isset($modSettings['load_average']))
1945
		$context['load_average'] = $modSettings['load_average'];
1946
1947
	// Detect the browser. This is separated out because it's also used in attachment downloads
1948
	detectBrowser();
1949
1950
	// Set the top level linktree up.
1951
	array_unshift($context['linktree'], array(
1952
		'url' => $scripturl,
1953
		'name' => $context['forum_name_html_safe']
1954
	));
1955
1956
	// This allows sticking some HTML on the page output - useful for controls.
1957
	$context['insert_after_template'] = '';
1958
1959
	if (!isset($txt))
1960
		$txt = array();
1961
1962
	$simpleActions = array(
1963
		'findmember',
1964
		'helpadmin',
1965
		'printpage',
1966
		'spellcheck',
1967
	);
1968
1969
	// Parent action => array of areas
1970
	$simpleAreas = array(
1971
		'profile' => array('popup', 'alerts_popup',),
1972
	);
1973
1974
	// Parent action => array of subactions
1975
	$simpleSubActions = array(
1976
		'pm' => array('popup',),
1977
		'signup' => array('usernamecheck'),
1978
	);
1979
1980
	// Extra params like ;preview ;js, etc.
1981
	$extraParams = array(
1982
		'preview',
1983
		'splitjs',
1984
	);
1985
1986
	// Actions that specifically uses XML output.
1987
	$xmlActions = array(
1988
		'quotefast',
1989
		'jsmodify',
1990
		'xmlhttp',
1991
		'post2',
1992
		'suggest',
1993
		'stats',
1994
		'notifytopic',
1995
		'notifyboard',
1996
	);
1997
1998
	call_integration_hook('integrate_simple_actions', array(&$simpleActions, &$simpleAreas, &$simpleSubActions, &$extraParams, &$xmlActions));
1999
2000
	$context['simple_action'] = in_array($context['current_action'], $simpleActions) ||
2001
	(isset($simpleAreas[$context['current_action']]) && isset($_REQUEST['area']) && in_array($_REQUEST['area'], $simpleAreas[$context['current_action']])) ||
2002
	(isset($simpleSubActions[$context['current_action']]) && in_array($context['current_subaction'], $simpleSubActions[$context['current_action']]));
2003
2004
	// See if theres any extra param to check.
2005
	$requiresXML = false;
2006
	foreach ($extraParams as $key => $extra)
2007
		if (isset($_REQUEST[$extra]))
2008
			$requiresXML = true;
2009
2010
	// Output is fully XML, so no need for the index template.
2011
	if (isset($_REQUEST['xml']) && (in_array($context['current_action'], $xmlActions) || $requiresXML))
2012
	{
2013
		loadLanguage('index+Modifications');
2014
		loadTemplate('Xml');
2015
		$context['template_layers'] = array();
2016
	}
2017
2018
	// These actions don't require the index template at all.
2019
	elseif (!empty($context['simple_action']))
2020
	{
2021
		loadLanguage('index+Modifications');
2022
		$context['template_layers'] = array();
2023
	}
2024
2025
	else
2026
	{
2027
		// Custom templates to load, or just default?
2028
		if (isset($settings['theme_templates']))
2029
			$templates = explode(',', $settings['theme_templates']);
2030
		else
2031
			$templates = array('index');
2032
2033
		// Load each template...
2034
		foreach ($templates as $template)
2035
			loadTemplate($template);
2036
2037
		// ...and attempt to load their associated language files.
2038
		$required_files = implode('+', array_merge($templates, array('Modifications')));
2039
		loadLanguage($required_files, '', false);
2040
2041
		// Custom template layers?
2042
		if (isset($settings['theme_layers']))
2043
			$context['template_layers'] = explode(',', $settings['theme_layers']);
2044
		else
2045
			$context['template_layers'] = array('html', 'body');
2046
	}
2047
2048
	// Initialize the theme.
2049
	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...
2050
2051
	// Allow overriding the board wide time/number formats.
2052
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
2053
		$user_info['time_format'] = $txt['time_format'];
2054
2055
	// Set the character set from the template.
2056
	$context['character_set'] = empty($modSettings['global_character_set']) ? $txt['lang_character_set'] : $modSettings['global_character_set'];
2057
	$context['utf8'] = $context['character_set'] === 'UTF-8';
2058
	$context['right_to_left'] = !empty($txt['lang_rtl']);
2059
2060
	// Guests may still need a name.
2061 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...
2062
		$context['user']['name'] = $txt['guest_title'];
2063
2064
	// Any theme-related strings that need to be loaded?
2065
	if (!empty($settings['require_theme_strings']))
2066
		loadLanguage('ThemeStrings', '', false);
2067
2068
	// Make a special URL for the language.
2069
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
2070
2071
	// And of course, let's load the default CSS file.
2072
	loadCSSFile('index.css', array('minimize' => true), 'smf_index');
2073
2074
	// Here is my luvly Responsive CSS
2075
	loadCSSFile('responsive.css', array('force_current' => false, 'validate' => true, 'minimize' => true), 'smf_responsive');
2076
2077
	if ($context['right_to_left'])
2078
		loadCSSFile('rtl.css', array(), 'smf_rtl');
2079
2080
	// We allow theme variants, because we're cool.
2081
	$context['theme_variant'] = '';
2082
	$context['theme_variant_url'] = '';
2083
	if (!empty($settings['theme_variants']))
2084
	{
2085
		// Overriding - for previews and that ilk.
2086
		if (!empty($_REQUEST['variant']))
2087
			$_SESSION['id_variant'] = $_REQUEST['variant'];
2088
		// User selection?
2089
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
2090
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
2091
		// If not a user variant, select the default.
2092
		if ($context['theme_variant'] == '' || !in_array($context['theme_variant'], $settings['theme_variants']))
2093
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
2094
2095
		// Do this to keep things easier in the templates.
2096
		$context['theme_variant'] = '_' . $context['theme_variant'];
2097
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
2098
2099
		if (!empty($context['theme_variant']))
2100
		{
2101
			loadCSSFile('index' . $context['theme_variant'] . '.css', array(), 'smf_index' . $context['theme_variant']);
2102
			if ($context['right_to_left'])
2103
				loadCSSFile('rtl' . $context['theme_variant'] . '.css', array(), 'smf_rtl' . $context['theme_variant']);
2104
		}
2105
	}
2106
2107
	// Let's be compatible with old themes!
2108
	if (!function_exists('template_html_above') && in_array('html', $context['template_layers']))
2109
		$context['template_layers'] = array('main');
2110
2111
	$context['tabindex'] = 1;
2112
2113
	// Compatibility.
2114
	if (!isset($settings['theme_version']))
2115
		$modSettings['memberCount'] = $modSettings['totalMembers'];
2116
2117
	// Default JS variables for use in every theme
2118
	$context['javascript_vars'] = array(
2119
		'smf_theme_url' => '"' . $settings['theme_url'] . '"',
2120
		'smf_default_theme_url' => '"' . $settings['default_theme_url'] . '"',
2121
		'smf_images_url' => '"' . $settings['images_url'] . '"',
2122
		'smf_smileys_url' => '"' . $modSettings['smileys_url'] . '"',
2123
		'smf_scripturl' => '"' . $scripturl . '"',
2124
		'smf_iso_case_folding' => $context['server']['iso_case_folding'] ? 'true' : 'false',
2125
		'smf_charset' => '"' . $context['character_set'] . '"',
2126
		'smf_session_id' => '"' . $context['session_id'] . '"',
2127
		'smf_session_var' => '"' . $context['session_var'] . '"',
2128
		'smf_member_id' => $context['user']['id'],
2129
		'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
2130
		'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
2131
	);
2132
2133
	// Add the JQuery library to the list of files to load.
2134
	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
2135
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2136
2137
	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
2138
		loadJavaScriptFile('jquery-2.1.4.min.js', array('seed' => false), 'smf_jquery');
2139
2140
	elseif (isset($modSettings['jquery_source'], $modSettings['jquery_custom']) && $modSettings['jquery_source'] == 'custom')
2141
		loadJavaScriptFile($modSettings['jquery_custom'], array(), 'smf_jquery');
2142
2143
	// Auto loading? template_javascript() will take care of the local half of this.
2144
	else
2145
		loadJavaScriptFile('https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js', array('external' => true), 'smf_jquery');
2146
2147
	// Queue our JQuery plugins!
2148
	loadJavaScriptFile('smf_jquery_plugins.js', array('minimize' => true), 'smf_jquery_plugins');
2149
	if (!$user_info['is_guest'])
2150
	{
2151
		loadJavaScriptFile('jquery.custom-scrollbar.js', array(), 'smf_jquery_scrollbar');
2152
		loadCSSFile('jquery.custom-scrollbar.css', array('force_current' => false, 'validate' => true), 'smf_scrollbar');
2153
	}
2154
2155
	// script.js and theme.js, always required, so always add them! Makes index.template.php cleaner and all.
2156
	loadJavaScriptFile('script.js', array('defer' => false, 'minimize' => true), 'smf_script');
2157
	loadJavaScriptFile('theme.js', array('minimize' => true), 'smf_theme');
2158
2159
	// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
2160
	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())
2161
	{
2162
		if (isBrowser('possibly_robot'))
2163
		{
2164
			// @todo Maybe move this somewhere better?!
2165
			require_once($sourcedir . '/ScheduledTasks.php');
2166
2167
			// What to do, what to do?!
2168
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
2169
				AutoTask();
2170
			else
2171
				ReduceMailQueue();
2172
		}
2173
		else
2174
		{
2175
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
2176
			$ts = $type == 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
2177
2178
			addInlineJavaScript('
2179
		function smfAutoTask()
2180
		{
2181
			$.get(smf_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '");
2182
		}
2183
		window.setTimeout("smfAutoTask();", 1);');
2184
		}
2185
	}
2186
2187
	// And we should probably trigger the cron too.
2188
	if (empty($modSettings['cron_is_real_cron']))
2189
	{
2190
		$ts = time();
2191
		$ts -= $ts % 15;
2192
		addInlineJavaScript('
2193
	function triggerCron()
2194
	{
2195
		$.get(' . JavaScriptEscape($boardurl) . ' + "/cron.php?ts=' . $ts . '");
2196
	}
2197
	window.setTimeout(triggerCron, 1);', true);
2198
	}
2199
2200
	// Filter out the restricted boards from the linktree
2201
	if (!$user_info['is_admin'] && !empty($board))
2202
	{
2203
		foreach ($context['linktree'] as $k => $element)
2204
		{
2205
			if (!empty($element['groups']) &&
2206
				(count(array_intersect($user_info['groups'], $element['groups'])) == 0 ||
2207
				(!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $element['deny_groups'])) != 0)))
2208
			{
2209
				$context['linktree'][$k]['name'] = $txt['restricted_board'];
2210
				$context['linktree'][$k]['extra_before'] = '<i>';
2211
				$context['linktree'][$k]['extra_after'] = '</i>';
2212
				unset($context['linktree'][$k]['url']);
2213
			}
2214
		}
2215
	}
2216
2217
	// Any files to include at this point?
2218 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...
2219
	{
2220
		$theme_includes = explode(',', $modSettings['integrate_theme_include']);
2221
		foreach ($theme_includes as $include)
2222
		{
2223
			$include = strtr(trim($include), array('$boarddir' => $boarddir, '$sourcedir' => $sourcedir, '$themedir' => $settings['theme_dir']));
2224
			if (file_exists($include))
2225
				require_once($include);
2226
		}
2227
	}
2228
2229
	// Call load theme integration functions.
2230
	call_integration_hook('integrate_load_theme');
2231
2232
	// We are ready to go.
2233
	$context['theme_loaded'] = true;
2234
}
2235
2236
/**
2237
 * Load a template - if the theme doesn't include it, use the default.
2238
 * What this function does:
2239
 *  - loads a template file with the name template_name from the current, default, or base theme.
2240
 *  - detects a wrong default theme directory and tries to work around it.
2241
 *
2242
 * @uses the template_include() function to include the file.
2243
 * @param string $template_name The name of the template to load
2244
 * @param array|string $style_sheets The name of a single stylesheet or an array of names of stylesheets to load
2245
 * @param bool $fatal If true, dies with an error message if the template cannot be found
2246
 * @return boolean Whether or not the template was loaded
2247
 */
2248
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
2249
{
2250
	global $context, $settings, $txt, $scripturl, $boarddir, $db_show_debug;
2251
2252
	// Do any style sheets first, cause we're easy with those.
2253
	if (!empty($style_sheets))
2254
	{
2255
		if (!is_array($style_sheets))
2256
			$style_sheets = array($style_sheets);
2257
2258
		foreach ($style_sheets as $sheet)
2259
			loadCSSFile($sheet . '.css', array(), $sheet);
2260
	}
2261
2262
	// No template to load?
2263
	if ($template_name === false)
2264
		return true;
2265
2266
	$loaded = false;
2267
	foreach ($settings['template_dirs'] as $template_dir)
2268
	{
2269
		if (file_exists($template_dir . '/' . $template_name . '.template.php'))
2270
		{
2271
			$loaded = true;
2272
			template_include($template_dir . '/' . $template_name . '.template.php', true);
2273
			break;
2274
		}
2275
	}
2276
2277
	if ($loaded)
2278
	{
2279
		if ($db_show_debug === true)
2280
			$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 2267. 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...
2281
2282
		// If they have specified an initialization function for this template, go ahead and call it now.
2283
		if (function_exists('template_' . $template_name . '_init'))
2284
			call_user_func('template_' . $template_name . '_init');
2285
	}
2286
	// Hmmm... doesn't exist?!  I don't suppose the directory is wrong, is it?
2287
	elseif (!file_exists($settings['default_theme_dir']) && file_exists($boarddir . '/Themes/default'))
2288
	{
2289
		$settings['default_theme_dir'] = $boarddir . '/Themes/default';
2290
		$settings['template_dirs'][] = $settings['default_theme_dir'];
2291
2292 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...
2293
		{
2294
			loadLanguage('Errors');
2295
			echo '
2296
<div class="alert errorbox">
2297
	<a href="', $scripturl . '?action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'], '" class="alert">', $txt['theme_dir_wrong'], '</a>
2298
</div>';
2299
		}
2300
2301
		loadTemplate($template_name);
2302
	}
2303
	// Cause an error otherwise.
2304
	elseif ($template_name != 'Errors' && $template_name != 'index' && $fatal)
2305
		fatal_lang_error('theme_template_error', 'template', array((string) $template_name));
2306
	elseif ($fatal)
2307
		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'));
2308
	else
2309
		return false;
2310
}
2311
2312
/**
2313
 * Load a sub-template.
2314
 * What it does:
2315
 * 	- loads the sub template specified by sub_template_name, which must be in an already-loaded template.
2316
 *  - if ?debug is in the query string, shows administrators a marker after every sub template
2317
 *	for debugging purposes.
2318
 *
2319
 * @todo get rid of reading $_REQUEST directly
2320
 *
2321
 * @param string $sub_template_name The name of the sub-template to load
2322
 * @param bool $fatal Whether to die with an error if the sub-template can't be loaded
2323
 */
2324
function loadSubTemplate($sub_template_name, $fatal = false)
2325
{
2326
	global $context, $txt, $db_show_debug;
2327
2328
	if ($db_show_debug === true)
2329
		$context['debug']['sub_templates'][] = $sub_template_name;
2330
2331
	// Figure out what the template function is named.
2332
	$theme_function = 'template_' . $sub_template_name;
2333
	if (function_exists($theme_function))
2334
		$theme_function();
2335
	elseif ($fatal === false)
2336
		fatal_lang_error('theme_template_error', 'template', array((string) $sub_template_name));
2337
	elseif ($fatal !== 'ignore')
2338
		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'));
2339
2340
	// Are we showing debugging for templates?  Just make sure not to do it before the doctype...
2341
	if (allowedTo('admin_forum') && isset($_REQUEST['debug']) && !in_array($sub_template_name, array('init', 'main_below')) && ob_get_length() > 0 && !isset($_REQUEST['xml']))
2342
	{
2343
		echo '
2344
<div class="warningbox">---- ', $sub_template_name, ' ends ----</div>';
2345
	}
2346
}
2347
2348
/**
2349
 * Add a CSS file for output later
2350
 *
2351
 * @param string $fileName The name of the file to load
2352
 * @param array $params An array of parameters
2353
 * Keys are the following:
2354
 * 	- ['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
2355
 * 	- ['default_theme'] (true/false): force use of default theme url
2356
 * 	- ['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
2357
 *  - ['validate'] (true/false): if true script will validate the local file exists
2358
 *  - ['rtl'] (string): additional file to load in RTL mode
2359
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2360
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2361
 * @param string $id An ID to stick on the end of the filename for caching purposes
2362
 */
2363
function loadCSSFile($fileName, $params = array(), $id = '')
2364
{
2365
	global $settings, $context, $modSettings;
2366
2367
	$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']) : '');
2368
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2369
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2370
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2371
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2372
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2373
2374
	// If this is an external file, automatically set this to false.
2375
	if (!empty($params['external']))
2376
		$params['minimize'] = false;
2377
2378
	// Account for shorthand like admin.css?alp21 filenames
2379
	$has_seed = strpos($fileName, '.css?');
2380
	$id = empty($id) ? strtr(basename(str_replace('.css', '', $fileName)), '?', '_') : $id;
2381
2382
	// Is this a local file?
2383 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...
2384
	{
2385
		// Are we validating the the file exists?
2386
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/css/' . $fileName))
2387
		{
2388
			// Maybe the default theme has it?
2389
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/css/' . $fileName))
2390
			{
2391
				$fileUrl = $settings['default_theme_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2392
				$filePath = $settings['default_theme_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2393
			}
2394
2395
			else
2396
				$fileUrl = false;
2397
		}
2398
2399
		else
2400
		{
2401
			$fileUrl = $settings[$themeRef . '_url'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2402
			$filePath = $settings[$themeRef . '_dir'] . '/css/' . $fileName . ($has_seed ? '' : $params['seed']);
2403
		}
2404
	}
2405
2406
	// An external file doesn't have a filepath. Mock one for simplicity.
2407
	else
2408
	{
2409
		$fileUrl = $fileName;
2410
		$filePath = $fileName;
2411
	}
2412
2413
	// Add it to the array for use in the template
2414 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...
2415
		$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...
2416
2417
	if (!empty($context['right_to_left']) && !empty($params['rtl']))
2418
		loadCSSFile($params['rtl'], array_diff_key($params, array('rtl' => 0)));
2419
}
2420
2421
/**
2422
 * Add a block of inline css code to be executed later
2423
 *
2424
 * - only use this if you have to, generally external css files are better, but for very small changes
2425
 *   or for scripts that require help from PHP/whatever, this can be useful.
2426
 * - all code added with this function is added to the same <style> tag so do make sure your css is valid!
2427
 *
2428
 * @param string $css Some css code
2429
 * @return void|bool Adds the CSS to the $context['css_header'] array or returns if no CSS is specified
2430
 */
2431
function addInlineCss($css)
2432
{
2433
	global $context;
2434
2435
	// Gotta add something...
2436
	if (empty($css))
2437
		return false;
2438
2439
	$context['css_header'][] = $css;
2440
}
2441
2442
/**
2443
 * Add a Javascript file for output later
2444
2445
 * @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...
2446
 * @param array $params An array of parameter info
2447
 * Keys are the following:
2448
 * 	- ['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
2449
 * 	- ['default_theme'] (true/false): force use of default theme url
2450
 * 	- ['defer'] (true/false): define if the file should load in <head> or before the closing <html> tag
2451
 * 	- ['force_current'] (true/false): if this is false, we will attempt to load the file from the
2452
 *	default theme if not found in the current theme
2453
 *	- ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
2454
 *  - ['validate'] (true/false): if true script will validate the local file exists
2455
 *  - ['seed'] (true/false/string): if true or null, use cache stale, false do not, or used a supplied string
2456
 *  - ['minimize'] boolean to add your file to the main minimized file. Useful when you have a file thats loaded everywhere and for everyone.
2457
 *
2458
 * @param string $id An ID to stick on the end of the filename
2459
 */
2460
function loadJavaScriptFile($fileName, $params = array(), $id = '')
2461
{
2462
	global $settings, $context, $modSettings;
2463
2464
	$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']) : '');
2465
	$params['force_current'] = isset($params['force_current']) ? $params['force_current'] : false;
2466
	$themeRef = !empty($params['default_theme']) ? 'default_theme' : 'theme';
2467
	$params['minimize'] = isset($params['minimize']) ? $params['minimize'] : false;
2468
	$params['external'] = isset($params['external']) ? $params['external'] : false;
2469
	$params['validate'] = isset($params['validate']) ? $params['validate'] : true;
2470
2471
	// If this is an external file, automatically set this to false.
2472
	if (!empty($params['external']))
2473
		$params['minimize'] = false;
2474
2475
	// Account for shorthand like admin.js?alp21 filenames
2476
	$has_seed = strpos($fileName, '.js?');
2477
	$id = empty($id) ? strtr(basename(str_replace('.js', '', $fileName)), '?', '_') : $id;
2478
2479
	// Is this a local file?
2480 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...
2481
	{
2482
		// Are we validating it exists on disk?
2483
		if (!empty($params['validate']) && !file_exists($settings[$themeRef . '_dir'] . '/scripts/' . $fileName))
2484
		{
2485
			// Can't find it in this theme, how about the default?
2486
			if ($themeRef === 'theme' && !$params['force_current'] && file_exists($settings['default_theme_dir'] . '/scripts/' . $fileName))
2487
			{
2488
				$fileUrl = $settings['default_theme_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2489
				$filePath = $settings['default_theme_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2490
			}
2491
2492
			else
2493
			{
2494
				$fileUrl = false;
2495
				$filePath = false;
2496
			}
2497
		}
2498
2499
		else
2500
		{
2501
			$fileUrl = $settings[$themeRef . '_url'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2502
			$filePath = $settings[$themeRef . '_dir'] . '/scripts/' . $fileName . ($has_seed ? '' : $params['seed']);
2503
		}
2504
	}
2505
2506
	// An external file doesn't have a filepath. Mock one for simplicity.
2507
	else
2508
	{
2509
		$fileUrl = $fileName;
2510
		$filePath = $fileName;
2511
	}
2512
2513
	// Add it to the array for use in the template
2514 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...
2515
		$context['javascript_files'][$id] = array('fileUrl' => $fileUrl, 'filePath' => $filePath, 'fileName' => $fileName, 'options' => $params);
2516
}
2517
2518
/**
2519
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2520
 * Cleaner and easier (for modders) than to use the function below.
2521
 *
2522
 * @param string $key The key for this variable
2523
 * @param string $value The value
2524
 * @param bool $escape Whether or not to escape the value
2525
 */
2526
function addJavaScriptVar($key, $value, $escape = false)
2527
{
2528
	global $context;
2529
2530
	if (!empty($key) && (!empty($value) || $value === '0'))
2531
		$context['javascript_vars'][$key] = !empty($escape) ? JavaScriptEscape($value) : $value;
2532
}
2533
2534
/**
2535
 * Add a block of inline Javascript code to be executed later
2536
 *
2537
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2538
 *   or for scripts that require help from PHP/whatever, this can be useful.
2539
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2540
 *
2541
 * @param string $javascript Some JS code
2542
 * @param bool $defer Whether the script should load in <head> or before the closing <html> tag
2543
 * @return void|bool Adds the code to one of the $context['javascript_inline'] arrays or returns if no JS was specified
2544
 */
2545
function addInlineJavaScript($javascript, $defer = false)
2546
{
2547
	global $context;
2548
2549
	if (empty($javascript))
2550
		return false;
2551
2552
	$context['javascript_inline'][($defer === true ? 'defer' : 'standard')][] = $javascript;
2553
}
2554
2555
/**
2556
 * Load a language file.  Tries the current and default themes as well as the user and global languages.
2557
 *
2558
 * @param string $template_name The name of a template file
2559
 * @param string $lang A specific language to load this file from
2560
 * @param bool $fatal Whether to die with an error if it can't be loaded
2561
 * @param bool $force_reload Whether to load the file again if it's already loaded
2562
 * @return string The language actually loaded.
2563
 */
2564
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2565
{
2566
	global $user_info, $language, $settings, $context, $modSettings;
2567
	global $db_show_debug, $sourcedir, $txt, $birthdayEmails, $txtBirthdayEmails;
2568
	static $already_loaded = array();
2569
2570
	// Default to the user's language.
2571
	if ($lang == '')
2572
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2573
2574
	// Do we want the English version of language file as fallback?
2575
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2576
		loadLanguage($template_name, 'english', false);
2577
2578
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2579
		return $lang;
2580
2581
	// Make sure we have $settings - if not we're in trouble and need to find it!
2582
	if (empty($settings['default_theme_dir']))
2583
	{
2584
		require_once($sourcedir . '/ScheduledTasks.php');
2585
		loadEssentialThemeData();
2586
	}
2587
2588
	// What theme are we in?
2589
	$theme_name = basename($settings['theme_url']);
2590
	if (empty($theme_name))
2591
		$theme_name = 'unknown';
2592
2593
	// For each file open it up and write it out!
2594
	foreach (explode('+', $template_name) as $template)
2595
	{
2596
		// Obviously, the current theme is most important to check.
2597
		$attempts = array(
2598
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2599
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2600
		);
2601
2602
		// Do we have a base theme to worry about?
2603
		if (isset($settings['base_theme_dir']))
2604
		{
2605
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2606
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2607
		}
2608
2609
		// Fall back on the default theme if necessary.
2610
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2611
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2612
2613
		// Fall back on the English language if none of the preferred languages can be found.
2614
		if (!in_array('english', array($lang, $language)))
2615
		{
2616
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2617
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2618
		}
2619
2620
		// Try to find the language file.
2621
		$found = false;
2622
		foreach ($attempts as $k => $file)
2623
		{
2624
			if (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2625
			{
2626
				// Include it!
2627
				template_include($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2628
2629
				// Note that we found it.
2630
				$found = true;
2631
2632
				break;
2633
			}
2634
		}
2635
2636
		// That couldn't be found!  Log the error, but *try* to continue normally.
2637
		if (!$found && $fatal)
2638
		{
2639
			log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2640
			break;
2641
		}
2642
2643
		// For the sake of backward compatibility
2644
		if (!empty($txt['emails']))
2645
		{
2646
			foreach ($txt['emails'] as $key => $value)
2647
			{
2648
				$txt[$key . '_subject'] = $value['subject'];
2649
				$txt[$key . '_body'] = $value['body'];
2650
			}
2651
			$txt['emails'] = array();
2652
		}
2653
		// For sake of backward compatibility: $birthdayEmails is supposed to be
2654
		// empty in a normal install. If it isn't it means the forum is using
2655
		// something "old" (it may be the translation, it may be a mod) and this
2656
		// code (like the piece above) takes care of converting it to the new format
2657
		if (!empty($birthdayEmails))
2658
		{
2659
			foreach ($birthdayEmails as $key => $value)
2660
			{
2661
				$txtBirthdayEmails[$key . '_subject'] = $value['subject'];
2662
				$txtBirthdayEmails[$key . '_body'] = $value['body'];
2663
				$txtBirthdayEmails[$key . '_author'] = $value['author'];
2664
			}
2665
			$birthdayEmails = array();
2666
		}
2667
	}
2668
2669
	// Keep track of what we're up to soldier.
2670
	if ($db_show_debug === true)
2671
		$context['debug']['language_files'][] = $template_name . '.' . $lang . ' (' . $theme_name . ')';
2672
2673
	// Remember what we have loaded, and in which language.
2674
	$already_loaded[$template_name] = $lang;
2675
2676
	// Return the language actually loaded.
2677
	return $lang;
2678
}
2679
2680
/**
2681
 * Get all parent boards (requires first parent as parameter)
2682
 * It finds all the parents of id_parent, and that board itself.
2683
 * Additionally, it detects the moderators of said boards.
2684
 *
2685
 * @param int $id_parent The ID of the parent board
2686
 * @return array An array of information about the boards found.
2687
 */
2688
function getBoardParents($id_parent)
2689
{
2690
	global $scripturl, $smcFunc;
2691
2692
	// First check if we have this cached already.
2693
	if (($boards = cache_get_data('board_parents-' . $id_parent, 480)) === null)
2694
	{
2695
		$boards = array();
2696
		$original_parent = $id_parent;
2697
2698
		// Loop while the parent is non-zero.
2699
		while ($id_parent != 0)
2700
		{
2701
			$result = $smcFunc['db_query']('', '
2702
				SELECT
2703
					b.id_parent, b.name, {int:board_parent} AS id_board, b.member_groups, b.deny_member_groups,
2704
					b.child_level, COALESCE(mem.id_member, 0) AS id_moderator, mem.real_name,
2705
					COALESCE(mg.id_group, 0) AS id_moderator_group, mg.group_name
2706
				FROM {db_prefix}boards AS b
2707
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2708
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2709
					LEFT JOIN {db_prefix}moderator_groups AS modgs ON (modgs.id_board = b.id_board)
2710
					LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = modgs.id_group)
2711
				WHERE b.id_board = {int:board_parent}',
2712
				array(
2713
					'board_parent' => $id_parent,
2714
				)
2715
			);
2716
			// In the EXTREMELY unlikely event this happens, give an error message.
2717
			if ($smcFunc['db_num_rows']($result) == 0)
2718
				fatal_lang_error('parent_not_found', 'critical');
2719
			while ($row = $smcFunc['db_fetch_assoc']($result))
2720
			{
2721
				if (!isset($boards[$row['id_board']]))
2722
				{
2723
					$id_parent = $row['id_parent'];
2724
					$boards[$row['id_board']] = array(
2725
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2726
						'name' => $row['name'],
2727
						'level' => $row['child_level'],
2728
						'groups' => explode(',', $row['member_groups']),
2729
						'deny_groups' => explode(',', $row['deny_member_groups']),
2730
						'moderators' => array(),
2731
						'moderator_groups' => array()
2732
					);
2733
				}
2734
				// If a moderator exists for this board, add that moderator for all children too.
2735 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...
2736
					foreach ($boards as $id => $dummy)
2737
					{
2738
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2739
							'id' => $row['id_moderator'],
2740
							'name' => $row['real_name'],
2741
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2742
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2743
						);
2744
					}
2745
2746
				// If a moderator group exists for this board, add that moderator group for all children too
2747 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...
2748
					foreach ($boards as $id => $dummy)
2749
					{
2750
						$boards[$id]['moderator_groups'][$row['id_moderator_group']] = array(
2751
							'id' => $row['id_moderator_group'],
2752
							'name' => $row['group_name'],
2753
							'href' => $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'],
2754
							'link' => '<a href="' . $scripturl . '?action=groups;sa=members;group=' . $row['id_moderator_group'] . '">' . $row['group_name'] . '</a>'
2755
						);
2756
					}
2757
			}
2758
			$smcFunc['db_free_result']($result);
2759
		}
2760
2761
		cache_put_data('board_parents-' . $original_parent, $boards, 480);
2762
	}
2763
2764
	return $boards;
2765
}
2766
2767
/**
2768
 * Attempt to reload our known languages.
2769
 * It will try to choose only utf8 or non-utf8 languages.
2770
 *
2771
 * @param bool $use_cache Whether or not to use the cache
2772
 * @param bool $favor_utf8 Whether or not to favor UTF-8 files
2773
 * @return array An array of information about available languages
2774
 */
2775
function getLanguages($use_cache = true, $favor_utf8 = true)
2776
{
2777
	global $context, $smcFunc, $settings, $modSettings;
2778
2779
	// Either we don't use the cache, or its expired.
2780
	if (!$use_cache || ($context['languages'] = cache_get_data('known_languages' . ($favor_utf8 ? '' : '_all'), !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600)) == null)
2781
	{
2782
		// If we don't have our ucwords function defined yet, let's load the settings data.
2783
		if (empty($smcFunc['ucwords']))
2784
			reloadSettings();
2785
2786
		// If we don't have our theme information yet, let's get it.
2787
		if (empty($settings['default_theme_dir']))
2788
			loadTheme(0, false);
2789
2790
		// Default language directories to try.
2791
		$language_directories = array(
2792
			$settings['default_theme_dir'] . '/languages',
2793
		);
2794
		if (!empty($settings['actual_theme_dir']) && $settings['actual_theme_dir'] != $settings['default_theme_dir'])
2795
			$language_directories[] = $settings['actual_theme_dir'] . '/languages';
2796
2797
		// We possibly have a base theme directory.
2798
		if (!empty($settings['base_theme_dir']))
2799
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2800
2801
		// Remove any duplicates.
2802
		$language_directories = array_unique($language_directories);
2803
2804
		// Get a list of languages.
2805
		$langList = !empty($modSettings['langList']) ? json_decode($modSettings['langList'], true) : array();
2806
		$langList = is_array($langList) ? $langList : false;
2807
2808
		$catchLang = array();
2809
2810
		foreach ($language_directories as $language_dir)
2811
		{
2812
			// Can't look in here... doesn't exist!
2813
			if (!file_exists($language_dir))
2814
				continue;
2815
2816
			$dir = dir($language_dir);
2817
			while ($entry = $dir->read())
2818
			{
2819
				// Look for the index language file....
2820
				if (!preg_match('~^index\.(.+)\.php$~', $entry, $matches))
2821
					continue;
2822
2823
				if (!empty($langList) && !empty($langList[$matches[1]]))
2824
					$langName = $langList[$matches[1]];
2825
2826
				else
2827
				{
2828
					$langName = $smcFunc['ucwords'](strtr($matches[1], array('_' => ' ')));
2829
2830
					// Get the line we need.
2831
					$fp = @fopen($language_dir .'/'. $entry);
2832
2833
					// Yay!
2834
					if ($fp)
2835
					{
2836
						while (($line = fgets($fp)) !== false)
2837
						{
2838
							preg_match('~\$txt\[\'native_name\'\] = \'(.+)\'\;~', $line, $matchNative);
2839
2840
							// Set the language's name.
2841
							if (!empty($matchNative) && !empty($matchNative[1]))
2842
							{
2843
								$langName = un_htmlspecialchars($matchNative[1]);
2844
								break;
2845
							}
2846
						}
2847
2848
						fclose($fp);
2849
					}
2850
2851
					// Catch the language name.
2852
					$catchLang[$matches[1]] = $langName;
2853
				}
2854
2855
				// Build this language entry.
2856
				$context['languages'][$matches[1]] = array(
2857
					'name' => $langName,
2858
					'selected' => false,
2859
					'filename' => $matches[1],
2860
					'location' => $language_dir . '/index.' . $matches[1] . '.php',
2861
				);
2862
2863
				$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...
2864
			}
2865
			$dir->close();
2866
		}
2867
2868
		// Do we need to store the lang list?
2869
		if (empty($langList))
2870
			updateSettings(array('langList' => json_encode($catchLang)));
2871
2872
		// Favoring UTF8? Then prevent us from selecting non-UTF8 versions.
2873
		if ($favor_utf8)
2874
		{
2875
			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...
2876
				if (substr($lang['filename'], strlen($lang['filename']) - 5, 5) != '-utf8' && isset($context['languages'][$lang['filename'] . '-utf8']))
2877
					unset($context['languages'][$lang['filename']]);
2878
		}
2879
2880
		// Let's cash in on this deal.
2881
		if (!empty($modSettings['cache_enable']))
2882
			cache_put_data('known_languages' . ($favor_utf8 ? '' : '_all'), $context['languages'], !empty($modSettings['cache_enable']) && $modSettings['cache_enable'] < 1 ? 86400 : 3600);
2883
	}
2884
2885
	return $context['languages'];
2886
}
2887
2888
/**
2889
 * Replace all vulgar words with respective proper words. (substring or whole words..)
2890
 * What this function does:
2891
 *  - it censors the passed string.
2892
 *  - if the theme setting allow_no_censored is on, and the theme option
2893
 *	show_no_censored is enabled, does not censor, unless force is also set.
2894
 *  - it caches the list of censored words to reduce parsing.
2895
 *
2896
 * @param string &$text The text to censor
2897
 * @param bool $force Whether to censor the text regardless of settings
2898
 * @return string The censored text
2899
 */
2900
function censorText(&$text, $force = false)
2901
{
2902
	global $modSettings, $options, $txt;
2903
	static $censor_vulgar = null, $censor_proper;
2904
2905
	if ((!empty($options['show_no_censored']) && !empty($modSettings['allow_no_censored']) && !$force) || empty($modSettings['censor_vulgar']) || trim($text) === '')
2906
		return $text;
2907
2908
	// If they haven't yet been loaded, load them.
2909
	if ($censor_vulgar == null)
2910
	{
2911
		$censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
2912
		$censor_proper = explode("\n", $modSettings['censor_proper']);
2913
2914
		// Quote them for use in regular expressions.
2915
		if (!empty($modSettings['censorWholeWord']))
2916
		{
2917
			for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++)
2918
			{
2919
				$censor_vulgar[$i] = str_replace(array('\\\\\\*', '\\*', '&', '\''), array('[*]', '[^\s]*?', '&amp;', '&#039;'), preg_quote($censor_vulgar[$i], '/'));
2920
				$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' : '');
2921
2922
				// @todo I'm thinking the old way is some kind of bug and this is actually fixing it.
2923
				//if (strpos($censor_vulgar[$i], '\'') !== false)
2924
					//$censor_vulgar[$i] = str_replace('\'', '&#039;', $censor_vulgar[$i]);
2925
			}
2926
		}
2927
	}
2928
2929
	// Censoring isn't so very complicated :P.
2930
	if (empty($modSettings['censorWholeWord']))
2931
	{
2932
		$func = !empty($modSettings['censorIgnoreCase']) ? 'str_ireplace' : 'str_replace';
2933
		$text = $func($censor_vulgar, $censor_proper, $text);
2934
	}
2935
	else
2936
		$text = preg_replace($censor_vulgar, $censor_proper, $text);
2937
2938
	return $text;
2939
}
2940
2941
/**
2942
 * Load the template/language file using eval or require? (with eval we can show an error message!)
2943
 * 	- loads the template or language file specified by filename.
2944
 * 	- uses eval unless disableTemplateEval is enabled.
2945
 * 	- outputs a parse error if the file did not exist or contained errors.
2946
 * 	- attempts to detect the error and line, and show detailed information.
2947
 *
2948
 * @param string $filename The name of the file to include
2949
 * @param bool $once If true only includes the file once (like include_once)
2950
 */
2951
function template_include($filename, $once = false)
2952
{
2953
	global $context, $settings, $txt, $scripturl, $modSettings;
2954
	global $boardurl, $boarddir, $sourcedir;
2955
	global $maintenance, $mtitle, $mmessage;
2956
	static $templates = array();
2957
2958
	// We want to be able to figure out any errors...
2959
	@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...
2960
2961
	// Don't include the file more than once, if $once is true.
2962
	if ($once && in_array($filename, $templates))
2963
		return;
2964
	// Add this file to the include list, whether $once is true or not.
2965
	else
2966
		$templates[] = $filename;
2967
2968
	// Are we going to use eval?
2969
	if (empty($modSettings['disableTemplateEval']))
2970
	{
2971
		$file_found = file_exists($filename) && eval('?' . '>' . rtrim(file_get_contents($filename))) !== false;
2972
		$settings['current_include_filename'] = $filename;
2973
	}
2974
	else
2975
	{
2976
		$file_found = file_exists($filename);
2977
2978
		if ($once && $file_found)
2979
			require_once($filename);
2980
		elseif ($file_found)
2981
			require($filename);
2982
	}
2983
2984
	if ($file_found !== true)
2985
	{
2986
		ob_end_clean();
2987
		if (!empty($modSettings['enableCompressedOutput']))
2988
			@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...
2989
		else
2990
			ob_start();
2991
2992 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...
2993
			header('Content-Type: application/xhtml+xml; charset=' . (empty($context['character_set']) ? 'ISO-8859-1' : $context['character_set']));
2994
2995
		// Don't cache error pages!!
2996
		header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
2997
		header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
2998
		header('Cache-Control: no-cache');
2999
3000
		if (!isset($txt['template_parse_error']))
3001
		{
3002
			$txt['template_parse_error'] = 'Template Parse Error!';
3003
			$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>.';
3004
			$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>.';
3005
			$txt['template_parse_errmsg'] = 'Unfortunately more information is not available at this time as to exactly what is wrong.';
3006
		}
3007
3008
		// First, let's get the doctype and language information out of the way.
3009
		echo '<!DOCTYPE html>
3010
<html', !empty($context['right_to_left']) ? ' dir="rtl"' : '', '>
3011
	<head>';
3012
		if (isset($context['character_set']))
3013
			echo '
3014
		<meta charset="', $context['character_set'], '">';
3015
3016
		if (!empty($maintenance) && !allowedTo('admin_forum'))
3017
			echo '
3018
		<title>', $mtitle, '</title>
3019
	</head>
3020
	<body>
3021
		<h3>', $mtitle, '</h3>
3022
		', $mmessage, '
3023
	</body>
3024
</html>';
3025
		elseif (!allowedTo('admin_forum'))
3026
			echo '
3027
		<title>', $txt['template_parse_error'], '</title>
3028
	</head>
3029
	<body>
3030
		<h3>', $txt['template_parse_error'], '</h3>
3031
		', $txt['template_parse_error_message'], '
3032
	</body>
3033
</html>';
3034
		else
3035
		{
3036
			require_once($sourcedir . '/Subs-Package.php');
3037
3038
			$error = fetch_web_data($boardurl . strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3039
			if (empty($error) && ini_get('track_errors') && !empty($php_errormsg))
3040
				$error = $php_errormsg;
3041
			if (empty($error))
3042
				$error = $txt['template_parse_errmsg'];
3043
3044
			$error = strtr($error, array('<b>' => '<strong>', '</b>' => '</strong>'));
3045
3046
			echo '
3047
		<title>', $txt['template_parse_error'], '</title>
3048
	</head>
3049
	<body>
3050
		<h3>', $txt['template_parse_error'], '</h3>
3051
		', sprintf($txt['template_parse_error_details'], strtr($filename, array($boarddir => '', strtr($boarddir, '\\', '/') => '')));
3052
3053
			if (!empty($error))
3054
				echo '
3055
		<hr>
3056
3057
		<div style="margin: 0 20px;"><pre>', strtr(strtr($error, array('<strong>' . $boarddir => '<strong>...', '<strong>' . strtr($boarddir, '\\', '/') => '<strong>...')), '\\', '/'), '</pre></div>';
3058
3059
			// I know, I know... this is VERY COMPLICATED.  Still, it's good.
3060
			if (preg_match('~ <strong>(\d+)</strong><br( /)?' . '>$~i', $error, $match) != 0)
3061
			{
3062
				$data = file($filename);
3063
				$data2 = highlight_php_code(implode('', $data));
3064
				$data2 = preg_split('~\<br( /)?\>~', $data2);
3065
3066
				// Fix the PHP code stuff...
3067
				if (!isBrowser('gecko'))
3068
					$data2 = str_replace("\t", '<span style="white-space: pre;">' . "\t" . '</span>', $data2);
3069
				else
3070
					$data2 = str_replace('<pre style="display: inline;">' . "\t" . '</pre>', "\t", $data2);
3071
3072
				// Now we get to work around a bug in PHP where it doesn't escape <br>s!
3073
				$j = -1;
3074
				foreach ($data as $line)
3075
				{
3076
					$j++;
3077
3078
					if (substr_count($line, '<br>') == 0)
3079
						continue;
3080
3081
					$n = substr_count($line, '<br>');
3082
					for ($i = 0; $i < $n; $i++)
3083
					{
3084
						$data2[$j] .= '&lt;br /&gt;' . $data2[$j + $i + 1];
3085
						unset($data2[$j + $i + 1]);
3086
					}
3087
					$j += $n;
3088
				}
3089
				$data2 = array_values($data2);
3090
				array_unshift($data2, '');
3091
3092
				echo '
3093
		<div style="margin: 2ex 20px; width: 96%; overflow: auto;"><pre style="margin: 0;">';
3094
3095
				// Figure out what the color coding was before...
3096
				$line = max($match[1] - 9, 1);
3097
				$last_line = '';
3098
				for ($line2 = $line - 1; $line2 > 1; $line2--)
3099
					if (strpos($data2[$line2], '<') !== false)
3100
					{
3101
						if (preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line2], $color_match) != 0)
3102
							$last_line = $color_match[1];
3103
						break;
3104
					}
3105
3106
				// Show the relevant lines...
3107
				for ($n = min($match[1] + 4, count($data2) + 1); $line <= $n; $line++)
3108
				{
3109
					if ($line == $match[1])
3110
						echo '</pre><div style="background-color: #ffb0b5;"><pre style="margin: 0;">';
3111
3112
					echo '<span style="color: black;">', sprintf('%' . strlen($n) . 's', $line), ':</span> ';
3113
					if (isset($data2[$line]) && $data2[$line] != '')
3114
						echo substr($data2[$line], 0, 2) == '</' ? preg_replace('~^</[^>]+>~', '', $data2[$line]) : $last_line . $data2[$line];
3115
3116
					if (isset($data2[$line]) && preg_match('~(<[^/>]+>)[^<]*$~', $data2[$line], $color_match) != 0)
3117
					{
3118
						$last_line = $color_match[1];
3119
						echo '</', substr($last_line, 1, 4), '>';
3120
					}
3121
					elseif ($last_line != '' && strpos($data2[$line], '<') !== false)
3122
						$last_line = '';
3123
					elseif ($last_line != '' && $data2[$line] != '')
3124
						echo '</', substr($last_line, 1, 4), '>';
3125
3126
					if ($line == $match[1])
3127
						echo '</pre></div><pre style="margin: 0;">';
3128
					else
3129
						echo "\n";
3130
				}
3131
3132
				echo '</pre></div>';
3133
			}
3134
3135
			echo '
3136
	</body>
3137
</html>';
3138
		}
3139
3140
		die;
3141
	}
3142
}
3143
3144
/**
3145
 * Initialize a database connection.
3146
 */
3147
function loadDatabase()
3148
{
3149
	global $db_persist, $db_connection, $db_server, $db_user, $db_passwd;
3150
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $sourcedir, $db_prefix, $db_port;
3151
3152
	// Figure out what type of database we are using.
3153
	if (empty($db_type) || !file_exists($sourcedir . '/Subs-Db-' . $db_type . '.php'))
3154
		$db_type = 'mysql';
3155
3156
	// Load the file for the database.
3157
	require_once($sourcedir . '/Subs-Db-' . $db_type . '.php');
3158
3159
	$db_options = array();
3160
3161
	// Add in the port if needed
3162
	if (!empty($db_port))
3163
		$db_options['port'] = $db_port;
3164
3165
	// 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.
3166
	if (SMF == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
3167
	{
3168
		$options = array_merge($db_options, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true));
3169
3170
		$db_connection = smf_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, $options);
3171
	}
3172
3173
	// Either we aren't in SSI mode, or it failed.
3174
	if (empty($db_connection))
3175
	{
3176
		$options = array_merge($db_options, array('persist' => $db_persist, 'dont_select_db' => SMF == 'SSI'));
3177
3178
		$db_connection = smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $options);
3179
	}
3180
3181
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
3182
	if (!$db_connection)
3183
		display_db_error();
3184
3185
	// If in SSI mode fix up the prefix.
3186
	if (SMF == 'SSI')
3187
		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...
3188
}
3189
3190
/**
3191
 * Try to load up a supported caching method. This is saved in $cacheAPI if we are not overriding it.
3192
 *
3193
 * @param string $overrideCache Try to use a different cache method other than that defined in $cache_accelerator.
3194
 * @param string $fallbackSMF Use the default SMF method if the accelerator fails.
3195
 * @return object A object of $cacheAPI.
3196
*/
3197
function loadCacheAccelerator($overrideCache = null, $fallbackSMF = true)
3198
{
3199
	global $sourcedir, $cacheAPI, $cache_accelerator;
3200
3201
	// Not overriding this and we have a cacheAPI, send it back.
3202
	if (empty($overrideCache) && is_object($cacheAPI))
3203
		return $cacheAPI;
3204
	elseif (is_null($cacheAPI))
3205
		$cacheAPI = false;
3206
3207
	// Make sure our class is in session.
3208
	require_once($sourcedir . '/Class-CacheAPI.php');
3209
3210
	// What accelerator we are going to try.
3211
	$tryAccelerator = !empty($overrideCache) ? $overrideCache : !empty($cache_accelerator) ? $cache_accelerator : 'smf';
3212
	$tryAccelerator = strtolower($tryAccelerator);
3213
3214
	// Do some basic tests.
3215
	if (file_exists($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php'))
3216
	{
3217
		require_once($sourcedir . '/CacheAPI-' . $tryAccelerator . '.php');
3218
3219
		$cache_class_name = $tryAccelerator . '_cache';
3220
		$testAPI = new $cache_class_name();
3221
3222
		// No Support?  NEXT!
3223
		if (!$testAPI->isSupported())
3224
		{
3225
			// Can we save ourselves?
3226
			if (!empty($fallbackSMF) && is_null($overrideCache) && $tryAccelerator != 'smf')
3227
				return loadCacheAccelerator(null, false);
3228
			return false;
3229
		}
3230
3231
		// Connect up to the accelerator.
3232
		$testAPI->connect();
3233
3234
		// Don't set this if we are overriding the cache.
3235
		if (is_null($overrideCache))
3236
		{
3237
			$cacheAPI = $testAPI;
3238
			return $cacheAPI;
3239
		}
3240
		else
3241
			return $testAPI;
3242
	}
3243
}
3244
3245
/**
3246
 * Try to retrieve a cache entry. On failure, call the appropriate function.
3247
 *
3248
 * @param string $key The key for this entry
3249
 * @param string $file The file associated with this entry
3250
 * @param string $function The function to call
3251
 * @param array $params Parameters to be passed to the specified function
3252
 * @param int $level The cache level
3253
 * @return string The cached data
3254
 */
3255
function cache_quick_get($key, $file, $function, $params, $level = 1)
3256
{
3257
	global $modSettings, $sourcedir;
3258
3259
	// @todo Why are we doing this if caching is disabled?
3260
3261
	if (function_exists('call_integration_hook'))
3262
		call_integration_hook('pre_cache_quick_get', array(&$key, &$file, &$function, &$params, &$level));
3263
3264
	/* Refresh the cache if either:
3265
		1. Caching is disabled.
3266
		2. The cache level isn't high enough.
3267
		3. The item has not been cached or the cached item expired.
3268
		4. The cached item has a custom expiration condition evaluating to true.
3269
		5. The expire time set in the cache item has passed (needed for Zend).
3270
	*/
3271
	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()))
3272
	{
3273
		require_once($sourcedir . '/' . $file);
3274
		$cache_block = call_user_func_array($function, $params);
3275
3276
		if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= $level)
3277
			cache_put_data($key, $cache_block, $cache_block['expires'] - time());
3278
	}
3279
3280
	// Some cached data may need a freshening up after retrieval.
3281
	if (!empty($cache_block['post_retri_eval']))
3282
		eval($cache_block['post_retri_eval']);
3283
3284
	if (function_exists('call_integration_hook'))
3285
		call_integration_hook('post_cache_quick_get', array(&$cache_block));
3286
3287
	return $cache_block['data'];
3288
}
3289
3290
/**
3291
 * Puts value in the cache under key for ttl seconds.
3292
 *
3293
 * - It may "miss" so shouldn't be depended on
3294
 * - Uses the cache engine chosen in the ACP and saved in settings.php
3295
 * - It supports:
3296
 *	 Xcache: http://xcache.lighttpd.net/wiki/XcacheApi
3297
 *	 memcache: http://www.php.net/memcache
3298
 *	 APC: http://www.php.net/apc
3299
 *   APCu: http://www.php.net/book.apcu
3300
 *	 Zend: http://files.zend.com/help/Zend-Platform/output_cache_functions.htm
3301
 *	 Zend: http://files.zend.com/help/Zend-Platform/zend_cache_functions.htm
3302
 *
3303
 * @param string $key A key for this value
3304
 * @param mixed $value The data to cache
3305
 * @param int $ttl How long (in seconds) the data should be cached for
3306
 */
3307
function cache_put_data($key, $value, $ttl = 120)
3308
{
3309
	global $boardurl, $modSettings, $cache_enable, $cacheAPI;
3310
	global $cache_hits, $cache_count, $db_show_debug;
3311
3312
	if (empty($cache_enable) || empty($cacheAPI))
3313
		return;
3314
3315
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3316
	if (isset($db_show_debug) && $db_show_debug === true)
3317
	{
3318
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'put', 's' => $value === null ? 0 : strlen(json_encode($value)));
3319
		$st = microtime();
3320
	}
3321
3322
	// The API will handle the rest.
3323
	$value = $value === null ? null : json_encode($value);
3324
	$result = $cacheAPI->putData($key, $value, $ttl);
0 ignored issues
show
Unused Code introduced by
$result 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...
3325
3326
	if (function_exists('call_integration_hook'))
3327
		call_integration_hook('cache_put_data', array(&$key, &$value, &$ttl));
3328
3329 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...
3330
		$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...
3331
}
3332
3333
/**
3334
 * Gets the value from the cache specified by key, so long as it is not older than ttl seconds.
3335
 * - It may often "miss", so shouldn't be depended on.
3336
 * - It supports the same as cache_put_data().
3337
 *
3338
 * @param string $key The key for the value to retrieve
3339
 * @param int $ttl The maximum age of the cached data
3340
 * @return string The cached data or null if nothing was loaded
3341
 */
3342
function cache_get_data($key, $ttl = 120)
3343
{
3344
	global $boardurl, $modSettings, $cache_enable, $cacheAPI;
3345
	global $cache_hits, $cache_count, $cache_misses, $cache_count_misses, $db_show_debug;
3346
3347
	if (empty($cache_enable) || empty($cacheAPI))
3348
		return;
3349
3350
	$cache_count = isset($cache_count) ? $cache_count + 1 : 1;
3351
	if (isset($db_show_debug) && $db_show_debug === true)
3352
	{
3353
		$cache_hits[$cache_count] = array('k' => $key, 'd' => 'get');
3354
		$st = microtime();
3355
		$original_key = $key;
3356
	}
3357
3358
	// Ask the API to get the data.
3359
	$value = $cacheAPI->getData($key, $ttl);
3360
3361
	if (isset($db_show_debug) && $db_show_debug === true)
3362
	{
3363
		$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...
3364
		$cache_hits[$cache_count]['s'] = isset($value) ? strlen($value) : 0;
3365
3366
		if (empty($value))
3367
		{
3368
			if (!is_array($cache_misses))
3369
				$cache_misses = array();
3370
3371
			$cache_count_misses = isset($cache_count_misses) ? $cache_count_misses + 1 : 1;
3372
			$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...
3373
		}
3374
	}
3375
3376
	if (function_exists('call_integration_hook') && isset($value))
3377
		call_integration_hook('cache_get_data', array(&$key, &$ttl, &$value));
3378
3379
	return empty($value) ? null : smf_json_decode($value, true);
3380
}
3381
3382
/**
3383
 * Empty out the cache in use as best it can
3384
 *
3385
 * It may only remove the files of a certain type (if the $type parameter is given)
3386
 * Type can be user, data or left blank
3387
 * 	- user clears out user data
3388
 *  - data clears out system / opcode data
3389
 *  - If no type is specified will perform a complete cache clearing
3390
 * For cache engines that do not distinguish on types, a full cache flush will be done
3391
 *
3392
 * @param string $type The cache type ('memcached', 'apc', 'xcache', 'zend' or something else for SMF's file cache)
3393
 */
3394
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...
3395
{
3396
	global $cachedir, $sourcedir, $modSettings;
3397
3398
	// If we can't get to the API, can't do this.
3399
	if (empty($cacheAPI))
0 ignored issues
show
Bug introduced by
The variable $cacheAPI seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
3400
		return false;
3401
3402
	// Ask the API to do the heavy lifting. cleanCache also calls invalidateCache to be sure.
3403
	$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...
3404
3405
	call_integration_hook('integrate_clean_cache');
3406
	clearstatcache();
3407
}
3408
3409
/**
3410
 * Helper function to set an array of data for an user's avatar.
3411
 *
3412
 * Makes assumptions based on the data provided, the following keys are required:
3413
 * - avatar The raw "avatar" column in members table
3414
 * - email The user's email. Used to get the gravatar info
3415
 * - filename The attachment filename
3416
 *
3417
 * @param array $data An array of raw info
3418
 * @return array An array of avatar data
3419
 */
3420
function set_avatar_data($data = array())
3421
{
3422
	global $modSettings, $boardurl, $smcFunc, $image_proxy_enabled, $image_proxy_secret;
3423
3424
	// Come on!
3425
	if (empty($data))
3426
		return array();
3427
3428
	// Set a nice default var.
3429
	$image = '';
3430
3431
	// Gravatar has been set as mandatory!
3432
	if (!empty($modSettings['gravatarOverride']))
3433
	{
3434
		if (!empty($modSettings['gravatarAllowExtraEmail']) && !empty($data['avatar']) && stristr($data['avatar'], 'gravatar://'))
3435
			$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3436
3437
		else if (!empty($data['email']))
3438
			$image = get_gravatar_url($data['email']);
3439
	}
3440
3441
	// Look if the user has a gravatar field or has set an external url as avatar.
3442
	else
3443
	{
3444
		// So it's stored in the member table?
3445
		if (!empty($data['avatar']))
3446
		{
3447
			// Gravatar.
3448
			if (stristr($data['avatar'], 'gravatar://'))
3449
			{
3450
				if ($data['avatar'] == 'gravatar://')
3451
					$image = get_gravatar_url($data['email']);
3452
3453
				elseif (!empty($modSettings['gravatarAllowExtraEmail']))
3454
					$image = get_gravatar_url($smcFunc['substr']($data['avatar'], 11));
3455
			}
3456
3457
			// External url.
3458
			else
3459
			{
3460
				// Using ssl?
3461
				if (!empty($modSettings['force_ssl']) && $image_proxy_enabled && stripos($data['avatar'], 'http://') !== false)
3462
					$image = strtr($boardurl, array('http://' => 'https://')) . '/proxy.php?request=' . urlencode($data['avatar']) . '&hash=' . md5($data['avatar'] . $image_proxy_secret);
3463
3464
				// Just a plain external url.
3465
				else
3466
					$image = (stristr($data['avatar'], 'http://') || stristr($data['avatar'], 'https://')) ? $data['avatar'] : $modSettings['avatar_url'] . '/' . $data['avatar'];
3467
			}
3468
		}
3469
3470
		// Perhaps this user has an attachment as avatar...
3471
		else if (!empty($data['filename']))
3472
			$image = $modSettings['custom_avatar_url'] . '/' . $data['filename'];
3473
3474
		// Right... no avatar... use our default image.
3475
		else
3476
			$image = $modSettings['avatar_url'] . '/default.png';
3477
	}
3478
3479
	call_integration_hook('integrate_set_avatar_data', array(&$image, &$data));
3480
3481
	// 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.
3482
	if (!empty($image))
3483
		return array(
3484
			'name' => !empty($data['avatar']) ? $data['avatar'] : '',
3485
			'image' => '<img class="avatar" src="' . $image . '" />',
3486
			'href' => $image,
3487
			'url' => $image,
3488
		);
3489
3490
	// Fallback to make life easier for everyone...
3491
	else
3492
		return array(
3493
			'name' => '',
3494
			'image' => '',
3495
			'href' => '',
3496
			'url' => '',
3497
		);
3498
}
3499
3500
?>
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...