Completed
Pull Request — development (#2907)
by Martyn
13:27
created

Load.php ➔ fix_calendar_text()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 65
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 57
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 57
nc 1
nop 0
dl 0
loc 65
ccs 57
cts 57
cp 1
crap 1
rs 9.3571
c 0
b 0
f 0

How to fix   Long Method   

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
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1 Release Candidate 1
15
 *
16
 */
17
18
/**
19
 * Load the $modSettings array and many necessary forum settings.
20
 *
21
 * What it does:
22
 *
23
 * - load the settings from cache if available, otherwise from the database.
24
 * - sets the timezone
25
 * - checks the load average settings if available.
26
 * - check whether post moderation is enabled.
27
 * - calls add_integration_function
28
 * - calls integrate_pre_include, integrate_pre_load,
29
 *
30
 * @global array $modSettings is a giant array of all of the forum-wide settings and statistics.
31
 */
32
function reloadSettings()
33
{
34
	global $modSettings;
35
36
	$db = database();
37
	$cache = Cache::instance();
38
	$hooks = Hooks::get();
39
40
	// Try to load it from the cache first; it'll never get cached if the setting is off.
41
	if (!$cache->getVar($modSettings, 'modSettings', 90))
42
	{
43
		$request = $db->query('', '
44
			SELECT variable, value
45
			FROM {db_prefix}settings',
46
			array(
47
			)
48
		);
49
		$modSettings = array();
50
		if (!$request)
51
			Errors::instance()->display_db_error();
52
		while ($row = $db->fetch_row($request))
53
			$modSettings[$row[0]] = $row[1];
54
		$db->free_result($request);
55
56
		// Do a few things to protect against missing settings or settings with invalid values...
57
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
58
			$modSettings['defaultMaxTopics'] = 20;
59 View Code Duplication
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
60
			$modSettings['defaultMaxMessages'] = 15;
61 View Code Duplication
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
62
			$modSettings['defaultMaxMembers'] = 30;
63
		if (empty($modSettings['subject_length']))
64
			$modSettings['subject_length'] = 24;
65
66
		$modSettings['warning_enable'] = $modSettings['warning_settings'][0];
67
68
		// @deprecated since 1.1.0 - Just in case the upgrade script was run before B3
69
		if (empty($modSettings['cal_limityear']))
70
		{
71
			$modSettings['cal_limityear'] = 10;
72
			updateSettings(array(
73
				'cal_limityear' => 10
74
			));
75
		}
76
77
		$cache->put('modSettings', $modSettings, 90);
78
	}
79
80
	$hooks->loadIntegrations();
81
82
	// Setting the timezone is a requirement for some functions in PHP >= 5.1.
83
	if (isset($modSettings['default_timezone']))
84
		date_default_timezone_set($modSettings['default_timezone']);
85
86
	// Check the load averages?
87
	if (!empty($modSettings['loadavg_enable']))
88
	{
89
		if (!$cache->getVar($modSettings['load_average'], 'loadavg', 90))
90
		{
91
			require_once(SUBSDIR . '/Server.subs.php');
92
			$modSettings['load_average'] = detectServerLoad();
93
94
			$cache->put('loadavg', $modSettings['load_average'], 90);
95
		}
96
97
		if ($modSettings['load_average'] !== false)
98
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
99
100
		// Let's have at least a zero
101
		if (empty($modSettings['loadavg_forum']) || $modSettings['load_average'] === false)
102
			$modSettings['current_load'] = 0;
103
		else
104
			$modSettings['current_load'] = $modSettings['load_average'];
105
106
		if (!empty($modSettings['loadavg_forum']) && $modSettings['current_load'] >= $modSettings['loadavg_forum'])
107
			Errors::instance()->display_loadavg_error();
108
	}
109
	else
110
		$modSettings['current_load'] = 0;
111
112
	// Is post moderation alive and well?
113
	$modSettings['postmod_active'] = isset($modSettings['admin_features']) ? in_array('pm', explode(',', $modSettings['admin_features'])) : true;
114
115
	// @deprecated since 1.0.6 compatibility setting for migration
116
	if (!isset($modSettings['avatar_max_height']))
117
	{
118
		$modSettings['avatar_max_height'] = isset($modSettings['avatar_max_height_external']) ? $modSettings['avatar_max_height_external'] : 65;
119
	}
120
	if (!isset($modSettings['avatar_max_width']))
121
	{
122
		$modSettings['avatar_max_width'] = isset($modSettings['avatar_max_width_external']) ? $modSettings['avatar_max_width_external'] : 65;
123
	}
124
125
	if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off')
126
	{
127
		$modSettings['secureCookies'] = 0;
128
	}
129
130
	// Here to justify the name of this function. :P
131
	// It should be added to the install and upgrade scripts.
132
	// But since the converters need to be updated also. This is easier.
133
	if (empty($modSettings['currentAttachmentUploadDir']))
134
	{
135
		updateSettings(array(
136
			'attachmentUploadDir' => serialize(array(1 => $modSettings['attachmentUploadDir'])),
137
			'currentAttachmentUploadDir' => 1,
138
		));
139
	}
140
141
	// Integration is cool.
142
	if (defined('ELK_INTEGRATION_SETTINGS'))
143
	{
144
		$integration_settings = Util::unserialize(ELK_INTEGRATION_SETTINGS);
145
		foreach ($integration_settings as $hook => $function)
146
			add_integration_function($hook, $function);
147
	}
148
149
	// Any files to pre include?
150
	call_integration_include_hook('integrate_pre_include');
151
152
	// Call pre load integration functions.
153
	call_integration_hook('integrate_pre_load');
154
}
155
156
/**
157
 * Load all the important user information.
158
 *
159
 * What it does:
160
 *
161
 * - sets up the $user_info array
162
 * - assigns $user_info['query_wanna_see_board'] for what boards the user can see.
163
 * - first checks for cookie or integration validation.
164
 * - uses the current session if no integration function or cookie is found.
165
 * - checks password length, if member is activated and the login span isn't over.
166
 * - if validation fails for the user, $id_member is set to 0.
167
 * - updates the last visit time when needed.
168
 */
169
function loadUserSettings()
170
{
171
	global $context, $modSettings, $user_settings, $cookiename, $user_info, $language;
172
173
	$db = database();
174
	$cache = Cache::instance();
175
176
	// Check first the integration, then the cookie, and last the session.
177
	if (count($integration_ids = call_integration_hook('integrate_verify_user')) > 0)
178
	{
179
		$id_member = 0;
180
		foreach ($integration_ids as $integration_id)
181
		{
182
			$integration_id = (int) $integration_id;
183
			if ($integration_id > 0)
184
			{
185
				$id_member = $integration_id;
186
				$already_verified = true;
187
				break;
188
			}
189
		}
190
	}
191
	else
192
		$id_member = 0;
193
194
	// We'll need IPs and user agent and stuff, they came to visit us with!
195
	$req = request();
196
197
	if (empty($id_member) && isset($_COOKIE[$cookiename]))
198
	{
199 View Code Duplication
		list ($id_member, $password) = serializeToJson($_COOKIE[$cookiename], function ($array_from) use ($cookiename) {
200
			global $modSettings;
201
202
			require_once(SUBSDIR . '/Auth.subs.php');
203
			$_COOKIE[$cookiename] = json_encode($array_from);
204
			setLoginCookie(60 * $modSettings['cookieTime'], $array_from[0], $array_from[1]);
205
		});
206
		$id_member = !empty($id_member) && strlen($password) > 0 ? (int) $id_member : 0;
207
	}
208
	elseif (empty($id_member) && isset($_SESSION['login_' . $cookiename]) && (!empty($modSettings['disableCheckUA']) || $_SESSION['USER_AGENT'] == $req->user_agent()))
209
	{
210
		// @todo Perhaps we can do some more checking on this, such as on the first octet of the IP?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
211
		list ($id_member, $password, $login_span) = serializeToJson($_SESSION['login_' . $cookiename], function ($array_from) use ($cookiename) {
212
			$_SESSION['login_' . $cookiename] = json_encode($array_from);
213
		});
214
		$id_member = !empty($id_member) && strlen($password) == 64 && $login_span > time() ? (int) $id_member : 0;
215
	}
216
217
	// Only load this stuff if the user isn't a guest.
218
	if ($id_member != 0)
219
	{
220
		// Is the member data cached?
221
		if ($cache->levelLowerThan(2) || $cache->getVar($user_settings, 'user_settings-' . $id_member, 60) === false)
222
		{
223
			$this_user = $db->fetchQuery('
224
				SELECT mem.*, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type
225
				FROM {db_prefix}members AS mem
226
					LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = {int:id_member})
227
				WHERE mem.id_member = {int:id_member}
228
				LIMIT 1',
229
				array(
230
					'id_member' => $id_member,
231
				)
232
			);
233
234
			if (!empty($this_user))
235
			{
236
				list ($user_settings) = $this_user;
237
238
				// Make the ID specifically an integer
239
				$user_settings['id_member'] = (int) $user_settings['id_member'];
240
			}
241
242
			if ($cache->levelHigherThan(1))
243
				$cache->put('user_settings-' . $id_member, $user_settings, 60);
244
		}
245
246
		// Did we find 'im?  If not, junk it.
247
		if (!empty($user_settings))
248
		{
249
			// As much as the password should be right, we can assume the integration set things up.
250
			if (!empty($already_verified) && $already_verified === true)
251
				$check = true;
252
			// SHA-256 passwords should be 64 characters long.
253
			elseif (strlen($password) == 64)
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...
254
				$check = hash('sha256', ($user_settings['passwd'] . $user_settings['password_salt'])) == $password;
255
			else
256
				$check = false;
257
258
			// Wrong password or not activated - either way, you're going nowhere.
259
			$id_member = $check && ($user_settings['is_activated'] == 1 || $user_settings['is_activated'] == 11) ? (int) $user_settings['id_member'] : 0;
260
		}
261
		else
262
			$id_member = 0;
263
264
		// If we no longer have the member maybe they're being all hackey, stop brute force!
265
		if (!$id_member)
266
			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);
0 ignored issues
show
Bug introduced by
It seems like !empty($user_settings['p...'passwd_flood'] : false can also be of type integer; however, validatePasswordFlood() does only seem to accept string|boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
267
	}
268
269
	// Found 'im, let's set up the variables.
270
	if ($id_member != 0)
271
	{
272
		// Let's not update the last visit time in these cases...
273
		// 1. SSI doesn't count as visiting the forum.
274
		// 2. RSS feeds and XMLHTTP requests don't count either.
275
		// 3. If it was set within this session, no need to set it again.
276
		// 4. New session, yet updated < five hours ago? Maybe cache can help.
277
		if (ELK != 'SSI' && !isset($_REQUEST['xml']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] != '.xml') && empty($_SESSION['id_msg_last_visit']) && (!$cache->isEnabled() || !$cache->getVar($_SESSION['id_msg_last_visit'], 'user_last_visit-' . $id_member, 5 * 3600)))
278
		{
279
			// @todo can this be cached?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
280
			// Do a quick query to make sure this isn't a mistake.
281
			require_once(SUBSDIR . '/Messages.subs.php');
282
			$visitOpt = basicMessageInfo($user_settings['id_msg_last_visit'], true);
283
284
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
285
286
			// If it was *at least* five hours ago...
287
			if ($visitOpt['poster_time'] < time() - 5 * 3600)
288
			{
289
				require_once(SUBSDIR . '/Members.subs.php');
290
				updateMemberData($id_member, array('id_msg_last_visit' => (int) $modSettings['maxMsgID'], 'last_login' => time(), 'member_ip' => $req->client_ip(), 'member_ip2' => $req->ban_ip()));
291
				$user_settings['last_login'] = time();
292
293
				if ($cache->levelHigherThan(1))
294
					$cache->put('user_settings-' . $id_member, $user_settings, 60);
295
296
				$cache->put('user_last_visit-' . $id_member, $_SESSION['id_msg_last_visit'], 5 * 3600);
297
			}
298
		}
299
		elseif (empty($_SESSION['id_msg_last_visit']))
300
			$_SESSION['id_msg_last_visit'] = $user_settings['id_msg_last_visit'];
301
302
		$username = $user_settings['member_name'];
303
304
		if (empty($user_settings['additional_groups']))
305
			$user_info = array(
306
				'groups' => array($user_settings['id_group'], $user_settings['id_post_group'])
307
			);
308
		else
309
			$user_info = array(
310
				'groups' => array_merge(
311
					array($user_settings['id_group'], $user_settings['id_post_group']),
312
					explode(',', $user_settings['additional_groups'])
313
				)
314
			);
315
316
		// Because history has proven that it is possible for groups to go bad - clean up in case.
317
		foreach ($user_info['groups'] as $k => $v)
318
			$user_info['groups'][$k] = (int) $v;
319
320
		// This is a logged in user, so definitely not a spider.
321
		$user_info['possibly_robot'] = false;
322
	}
323
	// If the user is a guest, initialize all the critical user settings.
324
	else
325
	{
326
		// This is what a guest's variables should be.
327
		$username = '';
328
		$user_info = array('groups' => array(-1));
329
		$user_settings = array();
330
331
		if (isset($_COOKIE[$cookiename]))
332
			$_COOKIE[$cookiename] = '';
333
334
		// Create a login token if it doesn't exist yet.
335
		if (!isset($_SESSION['token']['post-login']))
336
			createToken('login');
337
		else
338
			list ($context['login_token_var'],,, $context['login_token']) = $_SESSION['token']['post-login'];
339
340
		// Do we perhaps think this is a search robot? Check every five minutes just in case...
341
		if ((!empty($modSettings['spider_mode']) || !empty($modSettings['spider_group'])) && (!isset($_SESSION['robot_check']) || $_SESSION['robot_check'] < time() - 300))
342
		{
343
			require_once(SUBSDIR . '/SearchEngines.subs.php');
344
			$user_info['possibly_robot'] = spiderCheck();
345
		}
346
		elseif (!empty($modSettings['spider_mode']))
347
			$user_info['possibly_robot'] = isset($_SESSION['id_robot']) ? $_SESSION['id_robot'] : 0;
348
		// If we haven't turned on proper spider hunts then have a guess!
349
		else
350
		{
351
			$ci_user_agent = strtolower($req->user_agent());
352
			$user_info['possibly_robot'] = (strpos($ci_user_agent, 'mozilla') === false && strpos($ci_user_agent, 'opera') === false) || preg_match('~(googlebot|slurp|crawl|msnbot|yandex|bingbot|baidu)~u', $ci_user_agent) == 1;
353
		}
354
	}
355
356
	// Set up the $user_info array.
357
	$user_info += array(
358
		'id' => $id_member,
359
		'username' => $username,
360
		'name' => isset($user_settings['real_name']) ? $user_settings['real_name'] : '',
361
		'email' => isset($user_settings['email_address']) ? $user_settings['email_address'] : '',
362
		'passwd' => isset($user_settings['passwd']) ? $user_settings['passwd'] : '',
363
		'language' => empty($user_settings['lngfile']) || empty($modSettings['userLanguage']) ? $language : $user_settings['lngfile'],
364
		'is_guest' => $id_member == 0,
365
		'is_admin' => in_array(1, $user_info['groups']),
366
		'is_mod' => false,
367
		'theme' => empty($user_settings['id_theme']) ? 0 : $user_settings['id_theme'],
368
		'last_login' => empty($user_settings['last_login']) ? 0 : $user_settings['last_login'],
369
		'ip' => $req->client_ip(),
370
		'ip2' => $req->ban_ip(),
371
		'posts' => empty($user_settings['posts']) ? 0 : $user_settings['posts'],
372
		'time_format' => empty($user_settings['time_format']) ? $modSettings['time_format'] : $user_settings['time_format'],
373
		'time_offset' => empty($user_settings['time_offset']) ? 0 : $user_settings['time_offset'],
374
		'avatar' => array_merge(array(
375
			'url' => isset($user_settings['avatar']) ? $user_settings['avatar'] : '',
376
			'filename' => empty($user_settings['filename']) ? '' : $user_settings['filename'],
377
			'custom_dir' => !empty($user_settings['attachment_type']) && $user_settings['attachment_type'] == 1,
378
			'id_attach' => isset($user_settings['id_attach']) ? $user_settings['id_attach'] : 0
379
		), determineAvatar($user_settings)),
380
		'smiley_set' => isset($user_settings['smiley_set']) ? $user_settings['smiley_set'] : '',
381
		'messages' => empty($user_settings['personal_messages']) ? 0 : $user_settings['personal_messages'],
382
		'mentions' => empty($user_settings['mentions']) ? 0 : max(0, $user_settings['mentions']),
383
		'unread_messages' => empty($user_settings['unread_messages']) ? 0 : $user_settings['unread_messages'],
384
		'total_time_logged_in' => empty($user_settings['total_time_logged_in']) ? 0 : $user_settings['total_time_logged_in'],
385
		'buddies' => !empty($modSettings['enable_buddylist']) && !empty($user_settings['buddy_list']) ? explode(',', $user_settings['buddy_list']) : array(),
386
		'ignoreboards' => !empty($user_settings['ignore_boards']) && !empty($modSettings['allow_ignore_boards']) ? explode(',', $user_settings['ignore_boards']) : array(),
387
		'ignoreusers' => !empty($user_settings['pm_ignore_list']) ? explode(',', $user_settings['pm_ignore_list']) : array(),
388
		'warning' => isset($user_settings['warning']) ? $user_settings['warning'] : 0,
389
		'permissions' => array(),
390
	);
391
	$user_info['groups'] = array_unique($user_info['groups']);
392
393
	// 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.
394
	if (!empty($user_info['ignoreboards']) && empty($user_info['ignoreboards'][$tmp = count($user_info['ignoreboards']) - 1]))
395
		unset($user_info['ignoreboards'][$tmp]);
396
397
	// Do we have any languages to validate this?
398
	if (!empty($modSettings['userLanguage']) && (!empty($_GET['language']) || !empty($_SESSION['language'])))
399
		$languages = getLanguages();
400
401
	// Allow the user to change their language if its valid.
402
	if (!empty($modSettings['userLanguage']) && !empty($_GET['language']) && isset($languages[strtr($_GET['language'], './\\:', '____')]))
403
	{
404
		$user_info['language'] = strtr($_GET['language'], './\\:', '____');
405
		$_SESSION['language'] = $user_info['language'];
406
	}
407
	elseif (!empty($modSettings['userLanguage']) && !empty($_SESSION['language']) && isset($languages[strtr($_SESSION['language'], './\\:', '____')]))
408
		$user_info['language'] = strtr($_SESSION['language'], './\\:', '____');
409
410
	// Just build this here, it makes it easier to change/use - administrators can see all boards.
411
	if ($user_info['is_admin'])
412
		$user_info['query_see_board'] = '1=1';
413
	// Otherwise just the groups in $user_info['groups'].
414
	else
415
		$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'] : '') . ')';
416
	// Build the list of boards they WANT to see.
417
	// This will take the place of query_see_boards in certain spots, so it better include the boards they can see also
418
419
	// If they aren't ignoring any boards then they want to see all the boards they can see
420
	if (empty($user_info['ignoreboards']))
421
		$user_info['query_wanna_see_board'] = $user_info['query_see_board'];
422
	// Ok I guess they don't want to see all the boards
423
	else
424
		$user_info['query_wanna_see_board'] = '(' . $user_info['query_see_board'] . ' AND b.id_board NOT IN (' . implode(',', $user_info['ignoreboards']) . '))';
425
426
	call_integration_hook('integrate_user_info');
427
}
428
429
/**
430
 * Check for moderators and see if they have access to the board.
431
 *
432
 * What it does:
433
 *
434
 * - sets up the $board_info array for current board information.
435
 * - if cache is enabled, the $board_info array is stored in cache.
436
 * - redirects to appropriate post if only message id is requested.
437
 * - is only used when inside a topic or board.
438
 * - determines the local moderators for the board.
439
 * - adds group id 3 if the user is a local moderator for the board they are in.
440
 * - prevents access if user is not in proper group nor a local moderator of the board.
441
 */
442
function loadBoard()
443
{
444 6
	global $txt, $scripturl, $context, $modSettings;
445 6
	global $board_info, $board, $topic, $user_info;
446
447 6
	$db = database();
448 6
	$cache = Cache::instance();
449
450
	// Assume they are not a moderator.
451 6
	$user_info['is_mod'] = false;
452
	// @since 1.0.5 - is_mod takes into account only local (board) moderators,
453
	// and not global moderators, is_moderator is meant to take into account both.
454 6
	$user_info['is_moderator'] = false;
455
456
	// Start the linktree off empty..
457 6
	$context['linktree'] = array();
458
459
	// Have they by chance specified a message id but nothing else?
460 6
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
461 4
	{
462
		// Make sure the message id is really an int.
463
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
464
465
		// Looking through the message table can be slow, so try using the cache first.
466
		if (!$cache->getVar($topic, 'msg_topic-' . $_REQUEST['msg'], 120))
467
		{
468
			require_once(SUBSDIR . '/Messages.subs.php');
469
			$topic = associatedTopic($_REQUEST['msg']);
470
471
			// So did it find anything?
472
			if ($topic !== false)
473
			{
474
				// Save save save.
475
				$cache->put('msg_topic-' . $_REQUEST['msg'], $topic, 120);
476
			}
477
		}
478
479
		// Remember redirection is the key to avoiding fallout from your bosses.
480
		if (!empty($topic))
481
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
482
		else
483
		{
484
			loadPermissions();
485
			loadTheme();
486
			throw new Elk_Exception('topic_gone', false);
487
		}
488
	}
489
490
	// Load this board only if it is specified.
491 6
	if (empty($board) && empty($topic))
492 4
	{
493
		$board_info = array('moderators' => array());
494
		return;
495
	}
496
497 6
	if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2)))
498 4
	{
499
		// @todo SLOW?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
500
		if (!empty($topic))
501
			$temp = $cache->get('topic_board-' . $topic, 120);
502
		else
503
			$temp = $cache->get('board-' . $board, 120);
504
505
		if (!empty($temp))
506
		{
507
			$board_info = $temp;
508
			$board = (int) $board_info['id'];
509
		}
510
	}
511
512 6
	if (empty($temp))
513 4
	{
514 6
		$select_columns = array();
515 6
		$select_tables = array();
516
		// Wanna grab something more from the boards table or another table at all?
517 6
		call_integration_hook('integrate_load_board_query', array(&$select_columns, &$select_tables));
518
519 6
		$request = $db->query('', '
520
			SELECT
521
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
522
				b.id_parent, c.name AS cname, COALESCE(mem.id_member, 0) AS id_moderator,
523 6
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
524
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
525 6
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . (!empty($select_columns) ? ', ' . implode(', ', $select_columns) : '') . '
526 6
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
527 6
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . (!empty($select_tables) ? '
528 6
				' . implode("\n\t\t\t\t", $select_tables) : '') . '
529
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
530
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
531
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
532 6
			WHERE b.id_board = {raw:board_link}',
533
			array(
534 6
				'current_topic' => $topic,
535 6
				'board_link' => empty($topic) ? $db->quote('{int:current_board}', array('current_board' => $board)) : 't.id_board',
536
			)
537 4
		);
538
		// If there aren't any, skip.
539 6
		if ($db->num_rows($request) > 0)
540 4
		{
541 6
			$row = $db->fetch_assoc($request);
542
543
			// Set the current board.
544 6
			if (!empty($row['id_board']))
545 4
				$board = (int) $row['id_board'];
546
547
			// Basic operating information. (globals... :/)
548
			$board_info = array(
549 6
				'id' => $board,
550 4
				'moderators' => array(),
551
				'cat' => array(
552 6
					'id' => $row['id_cat'],
553 6
					'name' => $row['cname']
554 4
				),
555 6
				'name' => $row['bname'],
556 6
				'raw_description' => $row['description'],
557 6
				'description' => $row['description'],
558 6
				'num_topics' => $row['num_topics'],
559 6
				'unapproved_topics' => $row['unapproved_topics'],
560 6
				'unapproved_posts' => $row['unapproved_posts'],
561 6
				'unapproved_user_topics' => 0,
562 6
				'parent_boards' => getBoardParents($row['id_parent']),
563 6
				'parent' => $row['id_parent'],
564 6
				'child_level' => $row['child_level'],
565 6
				'theme' => $row['id_theme'],
566 6
				'override_theme' => !empty($row['override_theme']),
567 6
				'profile' => $row['id_profile'],
568 6
				'redirect' => $row['redirect'],
569 6
				'posts_count' => empty($row['count_posts']),
570 6
				'cur_topic_approved' => empty($topic) || $row['approved'],
571 6
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
572 4
			);
573
574
			// Load the membergroups allowed, and check permissions.
575 6
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
576 6
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
577
578 6
			call_integration_hook('integrate_loaded_board', array(&$board_info, &$row));
579
580
			do
581
			{
582 6
				if (!empty($row['id_moderator']))
583 4
					$board_info['moderators'][$row['id_moderator']] = array(
584
						'id' => $row['id_moderator'],
585
						'name' => $row['real_name'],
586
						'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
587
						'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
588
					);
589
			}
590 6
			while ($row = $db->fetch_assoc($request));
591
592
			// If the board only contains unapproved posts and the user can't approve then they can't see any topics.
593
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
594 6
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
595 4
			{
596
				// Free the previous result
597
				$db->free_result($request);
598
599
				// @todo why is this using id_topic?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
600
				// @todo Can this get cached?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
601
				$request = $db->query('', '
602
					SELECT COUNT(id_topic)
603
					FROM {db_prefix}topics
604
					WHERE id_member_started={int:id_member}
605
						AND approved = {int:unapproved}
606
						AND id_board = {int:board}',
607
					array(
608
						'id_member' => $user_info['id'],
609
						'unapproved' => 0,
610
						'board' => $board,
611
					)
612
				);
613
614
				list ($board_info['unapproved_user_topics']) = $db->fetch_row($request);
615
			}
616
617 6
			if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2)))
618 4
			{
619
				// @todo SLOW?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
620
				if (!empty($topic))
621
					$cache->put('topic_board-' . $topic, $board_info, 120);
622 2
				$cache->put('board-' . $board, $board_info, 120);
623
			}
624 4
		}
625
		else
626
		{
627
			// Otherwise the topic is invalid, there are no moderators, etc.
628
			$board_info = array(
629
				'moderators' => array(),
630
				'error' => 'exist'
631
			);
632
			$topic = null;
633
			$board = 0;
634
		}
635 6
		$db->free_result($request);
636 4
	}
637
638 6
	if (!empty($topic))
639 4
		$_GET['board'] = (int) $board;
640
641 6
	if (!empty($board))
642 4
	{
643
		// Now check if the user is a moderator.
644 6
		$user_info['is_mod'] = isset($board_info['moderators'][$user_info['id']]);
645
646 6 View Code Duplication
		if (count(array_intersect($user_info['groups'], $board_info['groups'])) == 0 && !$user_info['is_admin'])
647 4
			$board_info['error'] = 'access';
648 6 View Code Duplication
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect($user_info['groups'], $board_info['deny_groups'])) != 0 && !$user_info['is_admin'])
649 4
			$board_info['error'] = 'access';
650
651
		// Build up the linktree.
652 6
		$context['linktree'] = array_merge(
653 6
			$context['linktree'],
654
			array(array(
655 6
				'url' => $scripturl . '#c' . $board_info['cat']['id'],
656 6
				'name' => $board_info['cat']['name']
657 4
			)),
658 6
			array_reverse($board_info['parent_boards']),
659
			array(array(
660 6
				'url' => $scripturl . '?board=' . $board . '.0',
661 6
				'name' => $board_info['name']
662 4
			))
663 4
		);
664 4
	}
665
666
	// Set the template contextual information.
667 6
	$context['user']['is_mod'] = &$user_info['is_mod'];
668 6
	$context['user']['is_moderator'] = &$user_info['is_moderator'];
669 6
	$context['current_topic'] = $topic;
670 6
	$context['current_board'] = $board;
671
672
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
673 6
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || !$user_info['is_moderator']))
674 4
	{
675
		// The permissions and theme need loading, just to make sure everything goes smoothly.
676
		loadPermissions();
677
		loadTheme();
678
679
		$_GET['board'] = '';
680
		$_GET['topic'] = '';
681
682
		// The linktree should not give the game away mate!
683
		$context['linktree'] = array(
684
			array(
685
				'url' => $scripturl,
686
				'name' => $context['forum_name_html_safe']
687
			)
688
		);
689
690
		// If it's a prefetching agent, stop it
691
		stop_prefetching();
692
693
		// If we're requesting an attachment.
694
		if (!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...
695
		{
696
			ob_end_clean();
697
			header('HTTP/1.1 403 Forbidden');
698
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function loadBoard() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
699
		}
700
		elseif ($user_info['is_guest'])
701
		{
702
			loadLanguage('Errors');
703
			is_not_guest($txt['topic_gone']);
704
		}
705
		else
706
			throw new Elk_Exception('topic_gone', false);
707
	}
708
709 6
	if ($user_info['is_mod'])
710 4
		$user_info['groups'][] = 3;
711 6
}
712
713
/**
714
 * Load this user's permissions.
715
 *
716
 * What it does:
717
 *
718
 * - If the user is an admin, validate that they have not been banned.
719
 * - Attempt to load permissions from cache for cache level > 2
720
 * - See if the user is possibly a robot and apply added permissions as needed
721
 * - Load permissions from the general permissions table.
722
 * - If inside a board load the necessary board permissions.
723
 * - If the user is not a guest, identify what other boards they have access to.
724
 */
725
function loadPermissions()
726
{
727
	global $user_info, $board, $board_info, $modSettings;
728
729
	$db = database();
730
731
	if ($user_info['is_admin'])
732
	{
733
		banPermissions();
734
		return;
735
	}
736
737
	$removals = array();
738
739
	$cache = Cache::instance();
740
741
	if ($cache->isEnabled())
742
	{
743
		$cache_groups = $user_info['groups'];
744
		asort($cache_groups);
745
		$cache_groups = implode(',', $cache_groups);
746
747
		// If it's a spider then cache it different.
748
		if ($user_info['possibly_robot'])
749
			$cache_groups .= '-spider';
750
751
		$temp = array();
752
		if ($cache->levelHigherThan(1) && !empty($board) && $cache->getVar($temp, 'permissions:' . $cache_groups . ':' . $board, 240) && time() - 240 > $modSettings['settings_updated'])
753
		{
754
			list ($user_info['permissions']) = $temp;
755
			banPermissions();
756
757
			return;
758
		}
759
		elseif ($cache->getVar($temp, 'permissions:' . $cache_groups, 240) && time() - 240 > $modSettings['settings_updated'])
760
		{
761
			if (is_array($temp))
762
			{
763
				list ($user_info['permissions'], $removals) = $temp;
764
			}
765
		}
766
	}
767
768
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
769
	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
770
771
	if (empty($user_info['permissions']))
772
	{
773
		// Get the general permissions.
774
		$request = $db->query('', '
775
			SELECT permission, add_deny
776
			FROM {db_prefix}permissions
777
			WHERE id_group IN ({array_int:member_groups})
778
				' . $spider_restrict,
779
			array(
780
				'member_groups' => $user_info['groups'],
781
				'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0,
782
			)
783
		);
784 View Code Duplication
		while ($row = $db->fetch_assoc($request))
785
		{
786
			if (empty($row['add_deny']))
787
				$removals[] = $row['permission'];
788
			else
789
				$user_info['permissions'][] = $row['permission'];
790
		}
791
		$db->free_result($request);
792
793
		if (isset($cache_groups))
794
			$cache->put('permissions:' . $cache_groups, array($user_info['permissions'], !empty($removals) ? $removals : array()), 2);
795
	}
796
797
	// Get the board permissions.
798
	if (!empty($board))
799
	{
800
		// Make sure the board (if any) has been loaded by loadBoard().
801
		if (!isset($board_info['profile']))
802
			throw new Elk_Exception('no_board');
803
804
		$request = $db->query('', '
805
			SELECT permission, add_deny
806
			FROM {db_prefix}board_permissions
807
			WHERE (id_group IN ({array_int:member_groups})
808
				' . $spider_restrict . ')
809
				AND id_profile = {int:id_profile}',
810
			array(
811
				'member_groups' => $user_info['groups'],
812
				'id_profile' => $board_info['profile'],
813
				'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0,
814
			)
815
		);
816 View Code Duplication
		while ($row = $db->fetch_assoc($request))
817
		{
818
			if (empty($row['add_deny']))
819
				$removals[] = $row['permission'];
820
			else
821
				$user_info['permissions'][] = $row['permission'];
822
		}
823
		$db->free_result($request);
824
	}
825
826
	// Remove all the permissions they shouldn't have ;).
827 View Code Duplication
	if (!empty($modSettings['permission_enable_deny']))
828
		$user_info['permissions'] = array_diff($user_info['permissions'], $removals);
829
830
	if (isset($cache_groups) && !empty($board) && $cache->levelHigherThan(1))
831
		$cache->put('permissions:' . $cache_groups . ':' . $board, array($user_info['permissions'], null), 240);
832
833
	// Banned?  Watch, don't touch..
834
	banPermissions();
835
836
	// Load the mod cache so we can know what additional boards they should see, but no sense in doing it for guests
837
	if (!$user_info['is_guest'])
838
	{
839
		$user_info['is_moderator'] = $user_info['is_mod'] || allowedTo('moderate_board');
840
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
841
		{
842
			require_once(SUBSDIR . '/Auth.subs.php');
843
			rebuildModCache();
844
		}
845
		else
846
			$user_info['mod_cache'] = $_SESSION['mc'];
847
	}
848
}
849
850
/**
851
 * Loads an array of users' data by ID or member_name.
852
 *
853
 * @param int[]|int|string[]|string $users An array of users by id or name
854
 * @param bool $is_name = false $users is by name or by id
855
 * @param string $set = 'normal' What kind of data to load (normal, profile, minimal)
856
 * @return array|bool The ids of the members loaded or false
857
 */
858
function loadMemberData($users, $is_name = false, $set = 'normal')
859
{
860 12
	global $user_profile, $modSettings, $board_info, $context;
861
862 12
	$db = database();
863 12
	$cache = Cache::instance();
864
865
	// Can't just look for no users :P.
866 12
	if (empty($users))
867 8
		return false;
868
869
	// Pass the set value
870 12
	$context['loadMemberContext_set'] = $set;
871
872
	// Make sure it's an array.
873 12
	$users = !is_array($users) ? array($users) : array_unique($users);
874 12
	$loaded_ids = array();
875
876 12
	if (!$is_name && $cache->isEnabled() && $cache->levelHigherThan(2))
877 8
	{
878
		$users = array_values($users);
879
		for ($i = 0, $n = count($users); $i < $n; $i++)
880
		{
881
			$data = $cache->get('member_data-' . $set . '-' . $users[$i], 240);
882
			if ($cache->isMiss())
883
				continue;
884
885
			$loaded_ids[] = $data['id_member'];
886
			$user_profile[$data['id_member']] = $data;
887
			unset($users[$i]);
888
		}
889
	}
890
891
	// Used by default
892
	$select_columns = '
893
			COALESCE(lo.log_time, 0) AS is_online, COALESCE(a.id_attach, 0) AS id_attach, a.filename, a.attachment_type,
894
			mem.signature, mem.avatar, mem.id_member, mem.member_name,
895
			mem.real_name, mem.email_address, mem.hide_email, mem.date_registered, mem.website_title, mem.website_url,
896
			mem.birthdate, mem.member_ip, mem.member_ip2, mem.posts, mem.last_login, mem.likes_given, mem.likes_received,
897
			mem.karma_good, mem.id_post_group, mem.karma_bad, mem.lngfile, mem.id_group, mem.time_offset, mem.show_online,
898
			mg.online_color AS member_group_color, COALESCE(mg.group_name, {string:blank_string}) AS member_group,
899
			pg.online_color AS post_group_color, COALESCE(pg.group_name, {string:blank_string}) AS post_group,
900 12
			mem.is_activated, mem.warning, ' . (!empty($modSettings['titlesEnable']) ? 'mem.usertitle, ' : '') . '
901 12
			CASE WHEN mem.id_group = 0 OR mg.icons = {string:blank_string} THEN pg.icons ELSE mg.icons END AS icons';
902
	$select_tables = '
903
			LEFT JOIN {db_prefix}log_online AS lo ON (lo.id_member = mem.id_member)
904
			LEFT JOIN {db_prefix}attachments AS a ON (a.id_member = mem.id_member)
905
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
906 12
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)';
907
908
	// We add or replace according to the set
909
	switch ($set)
910
	{
911 12
		case 'normal':
912
			$select_columns .= ', mem.buddy_list';
913
			break;
914 12
		case 'profile':
915
			$select_columns .= ', mem.openid_uri, mem.id_theme, mem.pm_ignore_list, mem.pm_email_notify, mem.receive_from,
916
			mem.time_format, mem.secret_question, mem.additional_groups, mem.smiley_set,
917
			mem.total_time_logged_in, mem.notify_announcements, mem.notify_regularity, mem.notify_send_body,
918 12
			mem.notify_types, lo.url, mem.ignore_boards, mem.password_salt, mem.pm_prefs, mem.buddy_list, mem.otp_secret, mem.enable_otp';
919 12
			break;
920
		case 'minimal':
921
			$select_columns = '
922
			mem.id_member, mem.member_name, mem.real_name, mem.email_address, mem.hide_email, mem.date_registered,
923
			mem.posts, mem.last_login, mem.member_ip, mem.member_ip2, mem.lngfile, mem.id_group';
924
			$select_tables = '';
925
			break;
926
		default:
927
			trigger_error('loadMemberData(): Invalid member data set \'' . $set . '\'', E_USER_WARNING);
928
	}
929
930
	// Allow addons to easily add to the selected member data
931 12
	call_integration_hook('integrate_load_member_data', array(&$select_columns, &$select_tables, $set));
932
933 12
	if (!empty($users))
934 8
	{
935
		// Load the member's data.
936 12
		$request = $db->query('', '
937 12
			SELECT' . $select_columns . '
938 12
			FROM {db_prefix}members AS mem' . $select_tables . '
939 12
			WHERE mem.' . ($is_name ? 'member_name' : 'id_member') . (count($users) == 1 ? ' = {' . ($is_name ? 'string' : 'int') . ':users}' : ' IN ({' . ($is_name ? 'array_string' : 'array_int') . ':users})'),
940
			array(
941 12
				'blank_string' => '',
942 12
				'users' => count($users) == 1 ? current($users) : $users,
943
			)
944 8
		);
945 12
		$new_loaded_ids = array();
946 12
		while ($row = $db->fetch_assoc($request))
947
		{
948 12
			$new_loaded_ids[] = $row['id_member'];
949 12
			$loaded_ids[] = $row['id_member'];
950 12
			$row['options'] = array();
951 12
			$user_profile[$row['id_member']] = $row;
952 8
		}
953 12
		$db->free_result($request);
954 8
	}
955
956
	// Custom profile fields as well
957 12
	if (!empty($new_loaded_ids) && $set !== 'minimal' && (in_array('cp', $context['admin_features'])))
958 8
	{
959 6
		$request = $db->query('', '
960
			SELECT id_member, variable, value
961
			FROM {db_prefix}custom_fields_data
962 6
			WHERE id_member' . (count($new_loaded_ids) == 1 ? ' = {int:loaded_ids}' : ' IN ({array_int:loaded_ids})'),
963
			array(
964 6
				'loaded_ids' => count($new_loaded_ids) == 1 ? $new_loaded_ids[0] : $new_loaded_ids,
965
			)
966 4
		);
967 6
		while ($row = $db->fetch_assoc($request))
968
			$user_profile[$row['id_member']]['options'][$row['variable']] = $row['value'];
969 6
		$db->free_result($request);
970 4
	}
971
972
	// Anything else integration may want to add to the user_profile array
973 12
	if (!empty($new_loaded_ids))
974 12
		call_integration_hook('integrate_add_member_data', array($new_loaded_ids, $set));
975
976 12
	if (!empty($new_loaded_ids) && $cache->levelHigherThan(2))
977 8
	{
978
		for ($i = 0, $n = count($new_loaded_ids); $i < $n; $i++)
979
			$cache->put('member_data-' . $set . '-' . $new_loaded_ids[$i], $user_profile[$new_loaded_ids[$i]], 240);
980
	}
981
982
	// Are we loading any moderators?  If so, fix their group data...
983 12
	if (!empty($loaded_ids) && !empty($board_info['moderators']) && $set === 'normal' && count($temp_mods = array_intersect($loaded_ids, array_keys($board_info['moderators']))) !== 0)
984 8
	{
985
		$group_info = array();
986
		if (!$cache->getVar($group_info, 'moderator_group_info', 480))
987
		{
988
			require_once(SUBSDIR . '/Membergroups.subs.php');
989
			$group_info = membergroupById(3, true);
990
991
			$cache->put('moderator_group_info', $group_info, 480);
992
		}
993
994
		foreach ($temp_mods as $id)
995
		{
996
			// By popular demand, don't show admins or global moderators as moderators.
997
			if ($user_profile[$id]['id_group'] != 1 && $user_profile[$id]['id_group'] != 2)
998
				$user_profile[$id]['member_group'] = $group_info['group_name'];
999
1000
			// If the Moderator group has no color or icons, but their group does... don't overwrite.
1001
			if (!empty($group_info['icons']))
1002
				$user_profile[$id]['icons'] = $group_info['icons'];
1003
			if (!empty($group_info['online_color']))
1004
				$user_profile[$id]['member_group_color'] = $group_info['online_color'];
1005
		}
1006
	}
1007
1008 12
	return empty($loaded_ids) ? false : $loaded_ids;
1009
}
1010
1011
/**
1012
 * Loads the user's basic values... meant for template/theme usage.
1013
 *
1014
 * What it does:
1015
 *
1016
 * - Always loads the minimal values of username, name, id, href, link, email, show_email, registered, registered_timestamp
1017
 * - if $context['loadMemberContext_set'] is not minimal it will load in full a full set of user information
1018
 * - prepares signature for display (censoring if enabled)
1019
 * - loads in the members custom fields if any
1020
 * - prepares the users buddy list, including reverse buddy flags
1021
 *
1022
 * @param int $user
1023
 * @param bool $display_custom_fields = false
1024
 * @return boolean
1025
 */
1026
function loadMemberContext($user, $display_custom_fields = false)
1027
{
1028 6
	global $memberContext, $user_profile, $txt, $scripturl, $user_info;
1029 6
	global $context, $modSettings, $settings;
1030 6
	static $dataLoaded = array();
1031
1032
	// If this person's data is already loaded, skip it.
1033 6
	if (isset($dataLoaded[$user]))
1034 6
		return true;
1035
1036
	// We can't load guests or members not loaded by loadMemberData()!
1037 6
	if ($user == 0)
1038 4
		return false;
1039
1040 6
	if (!isset($user_profile[$user]))
1041 4
	{
1042
		trigger_error('loadMemberContext(): member id ' . $user . ' not previously loaded by loadMemberData()', E_USER_WARNING);
1043
		return false;
1044
	}
1045
1046 6
	$parsers = \BBC\ParserWrapper::getInstance();
1047
1048
	// Well, it's loaded now anyhow.
1049 6
	$dataLoaded[$user] = true;
1050 6
	$profile = $user_profile[$user];
1051
1052
	// Censor everything.
1053 6
	$profile['signature'] = censor($profile['signature']);
1054
1055
	// TODO: We should look into a censoring toggle for custom fields
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1056
1057
	// Set things up to be used before hand.
1058 6
	$profile['signature'] = str_replace(array("\n", "\r"), array('<br />', ''), $profile['signature']);
1059 6
	$profile['signature'] = $parsers->parseSignature($profile['signature'], true);
1060 6
	$profile['is_online'] = (!empty($profile['show_online']) || allowedTo('moderate_forum')) && $profile['is_online'] > 0;
1061 6
	$profile['icons'] = empty($profile['icons']) ? array('', '') : explode('#', $profile['icons']);
1062
1063
	// Setup the buddy status here (One whole in_array call saved :P)
1064 6
	$profile['buddy'] = in_array($profile['id_member'], $user_info['buddies']);
1065 6
	$buddy_list = !empty($profile['buddy_list']) ? explode(',', $profile['buddy_list']) : array();
1066
1067
	// These minimal values are always loaded
1068 6
	$memberContext[$user] = array(
1069 6
		'username' => $profile['member_name'],
1070 6
		'name' => $profile['real_name'],
1071 6
		'id' => $profile['id_member'],
1072 6
		'href' => $scripturl . '?action=profile;u=' . $profile['id_member'],
1073 6
		'link' => '<a href="' . $scripturl . '?action=profile;u=' . $profile['id_member'] . '" title="' . $txt['profile_of'] . ' ' . trim($profile['real_name']) . '">' . $profile['real_name'] . '</a>',
1074 6
		'email' => $profile['email_address'],
1075 6
		'show_email' => showEmailAddress(!empty($profile['hide_email']), $profile['id_member']),
1076 6
		'registered_raw' => empty($profile['date_registered']) ? 0 : $profile['date_registered'],
1077 6
		'registered' => empty($profile['date_registered']) ? $txt['not_applicable'] : standardTime($profile['date_registered']),
1078 6
		'registered_timestamp' => empty($profile['date_registered']) ? 0 : forum_time(true, $profile['date_registered']),
1079
	);
1080
1081
	// If the set isn't minimal then load the monstrous array.
1082 6
	if ($context['loadMemberContext_set'] !== 'minimal')
1083 4
	{
1084
		$memberContext[$user] += array(
1085 6
			'username_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['member_name'] . '</span>',
1086 6
			'name_color' => '<span ' . (!empty($profile['member_group_color']) ? 'style="color:' . $profile['member_group_color'] . ';"' : '') . '>' . $profile['real_name'] . '</span>',
1087 6
			'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>',
1088 6
			'is_buddy' => $profile['buddy'],
1089 6
			'is_reverse_buddy' => in_array($user_info['id'], $buddy_list),
1090 6
			'buddies' => $buddy_list,
1091 6
			'title' => !empty($modSettings['titlesEnable']) ? $profile['usertitle'] : '',
1092
			'website' => array(
1093 6
				'title' => $profile['website_title'],
1094 6
				'url' => $profile['website_url'],
1095 4
			),
1096 6
			'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']),
1097 6
			'signature' => $profile['signature'],
1098 6
			'real_posts' => $profile['posts'],
1099 6
			'posts' => comma_format($profile['posts']),
1100 6
			'avatar' => determineAvatar($profile),
1101 6
			'last_login' => empty($profile['last_login']) ? $txt['never'] : standardTime($profile['last_login']),
1102 6
			'last_login_timestamp' => empty($profile['last_login']) ? 0 : forum_time(false, $profile['last_login']),
1103
			'karma' => array(
1104 6
				'good' => $profile['karma_good'],
1105 6
				'bad' => $profile['karma_bad'],
1106 6
				'allow' => !$user_info['is_guest'] && !empty($modSettings['karmaMode']) && $user_info['id'] != $user && allowedTo('karma_edit') &&
1107 6
				($user_info['posts'] >= $modSettings['karmaMinPosts'] || $user_info['is_admin']),
1108 4
			),
1109
			'likes' => array(
1110 6
				'given' => $profile['likes_given'],
1111 6
				'received' => $profile['likes_received']
1112 4
			),
1113 6
			'ip' => htmlspecialchars($profile['member_ip'], ENT_COMPAT, 'UTF-8'),
1114 6
			'ip2' => htmlspecialchars($profile['member_ip2'], ENT_COMPAT, 'UTF-8'),
1115
			'online' => array(
1116 6
				'is_online' => $profile['is_online'],
1117 6
				'text' => Util::htmlspecialchars($txt[$profile['is_online'] ? 'online' : 'offline']),
1118 6
				'member_online_text' => sprintf($txt[$profile['is_online'] ? 'member_is_online' : 'member_is_offline'], Util::htmlspecialchars($profile['real_name'])),
1119 6
				'href' => $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'],
1120 6
				'link' => '<a href="' . $scripturl . '?action=pm;sa=send;u=' . $profile['id_member'] . '">' . $txt[$profile['is_online'] ? 'online' : 'offline'] . '</a>',
1121 6
				'label' => $txt[$profile['is_online'] ? 'online' : 'offline']
1122 4
			),
1123 6
			'language' => Util::ucwords(strtr($profile['lngfile'], array('_' => ' '))),
1124 6
			'is_activated' => isset($profile['is_activated']) ? $profile['is_activated'] : 1,
1125 6
			'is_banned' => isset($profile['is_activated']) ? $profile['is_activated'] >= 10 : 0,
1126 6
			'options' => $profile['options'],
1127 4
			'is_guest' => false,
1128 6
			'group' => $profile['member_group'],
1129 6
			'group_color' => $profile['member_group_color'],
1130 6
			'group_id' => $profile['id_group'],
1131 6
			'post_group' => $profile['post_group'],
1132 6
			'post_group_color' => $profile['post_group_color'],
1133 6
			'group_icons' => str_repeat('<img src="' . str_replace('$language', $context['user']['language'], isset($profile['icons'][1]) ? $settings['images_url'] . '/group_icons/' . $profile['icons'][1] : '') . '" alt="[*]" />', empty($profile['icons'][0]) || empty($profile['icons'][1]) ? 0 : $profile['icons'][0]),
1134 6
			'warning' => $profile['warning'],
1135 6
			'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' : (''))),
1136 6
			'local_time' => standardTime(time() + ($profile['time_offset'] - $user_info['time_offset']) * 3600, false),
1137 4
			'custom_fields' => array(),
1138
		);
1139 4
	}
1140
1141
	// Are we also loading the members custom fields into context?
1142 6
	if ($display_custom_fields && !empty($modSettings['displayFields']))
1143 4
	{
1144
		if (!isset($context['display_fields']))
1145
			$context['display_fields'] = Util::unserialize($modSettings['displayFields']);
1146
1147
		foreach ($context['display_fields'] as $custom)
1148
		{
1149
			if (!isset($custom['title']) || trim($custom['title']) == '' || empty($profile['options'][$custom['colname']]))
1150
				continue;
1151
1152
			$value = $profile['options'][$custom['colname']];
1153
1154
			// BBC?
1155
			if ($custom['bbc'])
1156
				$value = $parsers->parseCustomFields($value);
1157
			// ... or checkbox?
1158
			elseif (isset($custom['type']) && $custom['type'] == 'check')
1159
				$value = $value ? $txt['yes'] : $txt['no'];
1160
1161
			// Enclosing the user input within some other text?
1162 View Code Duplication
			if (!empty($custom['enclose']))
1163
				$value = strtr($custom['enclose'], array(
1164
					'{SCRIPTURL}' => $scripturl,
1165
					'{IMAGES_URL}' => $settings['images_url'],
1166
					'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1167
					'{INPUT}' => $value,
1168
				));
1169
1170
			$memberContext[$user]['custom_fields'][] = array(
1171
				'title' => $custom['title'],
1172
				'colname' => $custom['colname'],
1173
				'value' => $value,
1174
				'placement' => !empty($custom['placement']) ? $custom['placement'] : 0,
1175
			);
1176
		}
1177
	}
1178
1179 6
	call_integration_hook('integrate_member_context', array($user, $display_custom_fields));
1180 6
	return true;
1181
}
1182
1183
/**
1184
 * Loads information about what browser the user is viewing with and places it in $context
1185
 *
1186
 * @uses the class from BrowserDetect.class.php
1187
 */
1188
function detectBrowser()
1189
{
1190
	// Load the current user's browser of choice
1191 42
	$detector = new Browser_Detector;
1192 42
	$detector->detectBrowser();
1193 42
}
1194
1195
/**
1196
 * Get the id of a theme
1197
 *
1198
 * @param int $id_theme
1199
 * @return int
1200
 */
1201
function getThemeId($id_theme = 0)
1202
{
1203 42
	global $modSettings, $user_info, $board_info, $ssi_theme;
1204
1205
	// The theme was specified by parameter.
1206 42
	if (!empty($id_theme))
1207 28
		$id_theme = (int) $id_theme;
1208
	// The theme was specified by REQUEST.
1209 42 View Code Duplication
	elseif (!empty($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1210
	{
1211
		$id_theme = (int) $_REQUEST['theme'];
1212
		$_SESSION['id_theme'] = $id_theme;
1213
	}
1214
	// The theme was specified by REQUEST... previously.
1215 42 View Code Duplication
	elseif (!empty($_SESSION['id_theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1216
		$id_theme = (int) $_SESSION['id_theme'];
1217
	// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
1218 42 View Code Duplication
	elseif (!empty($user_info['theme']) && !isset($_REQUEST['theme']) && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')))
1219
		$id_theme = $user_info['theme'];
1220
	// The theme was specified by the board.
1221 42
	elseif (!empty($board_info['theme']))
1222
		$id_theme = $board_info['theme'];
1223
	// The theme is the forum's default.
1224
	else
1225 42
		$id_theme = $modSettings['theme_guests'];
1226
1227
	// Verify the id_theme... no foul play.
1228
	// Always allow the board specific theme, if they are overriding.
1229 42
	if (!empty($board_info['theme']) && $board_info['override_theme'])
1230 28
		$id_theme = $board_info['theme'];
1231
	// If they have specified a particular theme to use with SSI allow it to be used.
1232 42
	elseif (!empty($ssi_theme) && $id_theme == $ssi_theme)
1233
		$id_theme = (int) $id_theme;
1234 42 View Code Duplication
	elseif (!empty($modSettings['knownThemes']) && !allowedTo('admin_forum'))
1235
	{
1236 24
		$themes = explode(',', $modSettings['knownThemes']);
1237 24
		if (!in_array($id_theme, $themes))
1238 16
			$id_theme = $modSettings['theme_guests'];
1239
		else
1240 24
			$id_theme = (int) $id_theme;
1241 16
	}
1242
	else
1243 18
		$id_theme = (int) $id_theme;
1244
1245 42
	return $id_theme;
1246
}
1247
1248
/**
1249
 * Load in the theme variables for a given theme / member combination
1250
 *
1251
 * @param int $id_theme
1252
 * @param int $member
1253
 *
1254
 * @return array
1255
 */
1256
function getThemeData($id_theme, $member)
1257
{
1258 42
	global $modSettings, $boardurl;
1259
1260 42
	$cache = Cache::instance();
1261
1262
	// Do we already have this members theme data and specific options loaded (for aggressive cache settings)
1263 42
	$temp = array();
1264 42
	if ($cache->levelHigherThan(1) && $cache->getVar($temp, 'theme_settings-' . $id_theme . ':' . $member, 60) && time() - 60 > $modSettings['settings_updated'])
1265 28
	{
1266
		$themeData = $temp;
1267
		$flag = true;
1268
	}
1269
	// Or do we just have the system wide theme settings cached
1270 42
	elseif ($cache->getVar($temp, 'theme_settings-' . $id_theme, 90) && time() - 60 > $modSettings['settings_updated'])
1271
		$themeData = $temp + array($member => array());
1272
	// Nothing at all then
1273
	else
1274 42
		$themeData = array(-1 => array(), 0 => array(), $member => array());
1275
1276 42
	if (empty($flag))
1277 28
	{
1278 42
		$db = database();
1279
1280
		// Load variables from the current or default theme, global or this user's.
1281 42
		$result = $db->query('', '
1282
			SELECT variable, value, id_member, id_theme
1283
			FROM {db_prefix}themes
1284 42
			WHERE id_member' . (empty($themeData[0]) ? ' IN (-1, 0, {int:id_member})' : ' = {int:id_member}') . '
1285 42
				AND id_theme' . ($id_theme == 1 ? ' = {int:id_theme}' : ' IN ({int:id_theme}, 1)'),
1286
			array(
1287 42
				'id_theme' => $id_theme,
1288 42
				'id_member' => $member,
1289
			)
1290 28
		);
1291
1292 42
		$immutable_theme_data = 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');
1293
1294
		// Pick between $settings and $options depending on whose data it is.
1295 42
		while ($row = $db->fetch_assoc($result))
1296
		{
1297
			// There are just things we shouldn't be able to change as members.
1298 42
			if ($row['id_member'] != 0 && in_array($row['variable'], $immutable_theme_data))
1299 28
				continue;
1300
1301
			// If this is the theme_dir of the default theme, store it.
1302 42
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1' && empty($row['id_member']))
1303 42
				$themeData[0]['default_' . $row['variable']] = $row['value'];
1304
1305
			// If this isn't set yet, is a theme option, or is not the default theme..
1306 42
			if (!isset($themeData[$row['id_member']][$row['variable']]) || $row['id_theme'] != '1')
1307 42
				$themeData[$row['id_member']][$row['variable']] = substr($row['variable'], 0, 5) == 'show_' ? $row['value'] == '1' : $row['value'];
1308 28
		}
1309 42
		$db->free_result($result);
1310
1311 42
		if (file_exists($themeData[0]['default_theme_dir'] . '/cache') && is_writable($themeData[0]['default_theme_dir'] . '/cache'))
1312 28
		{
1313 42
			$themeData[0]['default_theme_cache_dir'] = $themeData[0]['default_theme_dir'] . '/cache';
1314 42
			$themeData[0]['default_theme_cache_url'] = $themeData[0]['default_theme_url'] . '/cache';
1315 28
		}
1316
		else
1317
		{
1318
			$themeData[0]['default_theme_cache_dir'] = CACHEDIR;
1319
			$themeData[0]['default_theme_cache_url'] = $boardurl . '/cache';
1320
		}
1321
1322
		// Set the defaults if the user has not chosen on their own
1323 42
		if (!empty($themeData[-1]))
1324 28
		{
1325 42
			foreach ($themeData[-1] as $k => $v)
1326
			{
1327 42
				if (!isset($themeData[$member][$k]))
1328 42
					$themeData[$member][$k] = $v;
1329 28
			}
1330 28
		}
1331
1332
		// If being aggressive we save the site wide and member theme settings
1333 42
		if ($cache->levelHigherThan(1))
1334 28
			$cache->put('theme_settings-' . $id_theme . ':' . $member, $themeData, 60);
1335
		// Only if we didn't already load that part of the cache...
1336 42
		elseif (!isset($temp))
1337 42
			$cache->put('theme_settings-' . $id_theme, array(-1 => $themeData[-1], 0 => $themeData[0]), 90);
1338 28
	}
1339
1340 42
	return $themeData;
1341
}
1342
1343
/**
1344
 * Initialize a theme for use
1345
 *
1346
 * @param int $id_theme
1347
 */
1348
function initTheme($id_theme = 0)
1349
{
1350 42
	global $user_info, $settings, $options, $context;
1351
1352
	// Validate / fetch the themes id
1353 42
	$id_theme = getThemeId($id_theme);
1354
1355
	// Need to know who we are loading the theme for
1356 42
	$member = empty($user_info['id']) ? -1 : $user_info['id'];
1357
1358
	// Load in the theme variables for them
1359 42
	$themeData = getThemeData($id_theme, $member);
1360 42
	$settings = $themeData[0];
1361 42
	$options = $themeData[$member];
1362
1363 42
	$settings['theme_id'] = $id_theme;
1364 42
	$settings['actual_theme_url'] = $settings['theme_url'];
1365 42
	$settings['actual_images_url'] = $settings['images_url'];
1366 42
	$settings['actual_theme_dir'] = $settings['theme_dir'];
1367
1368
	// Reload the templates
1369 42
	Templates::getInstance()->reloadDirectories($settings);
1370
1371
	// Setup the default theme file. In the future, this won't exist and themes will just have to extend it if they want
1372 42
	require_once($settings['default_theme_dir'] . '/Theme.php');
1373 42
	$default_theme_instance = new \Themes\DefaultTheme\Theme(1);
1374
1375
	// Check if there is a Theme file
1376 42
	if ($id_theme != 1 && !empty($settings['theme_dir']) && file_exists($settings['theme_dir'] . '/Theme.php'))
1377 28
	{
1378
		require_once($settings['theme_dir'] . '/Theme.php');
1379
1380
		$class = '\\Themes\\' . basename(ucfirst($settings['theme_dir'])) . 'Theme\\Theme';
1381
1382
		$theme = new $class($id_theme);
1383
1384
		$context['theme_instance'] = $theme;
1385
	}
1386
	else
1387
	{
1388 42
		$context['theme_instance'] = $default_theme_instance;
1389
	}
1390 42
}
1391
1392
/**
1393
 * Load a theme, by ID.
1394
 *
1395
 * What it does:
1396
 *
1397
 * - identify the theme to be loaded.
1398
 * - validate that the theme is valid and that the user has permission to use it
1399
 * - load the users theme settings and site settings into $options.
1400
 * - prepares the list of folders to search for template loading.
1401
 * - identify what smiley set to use.
1402
 * - sets up $context['user']
1403
 * - detects the users browser and sets a mobile friendly environment if needed
1404
 * - loads default JS variables for use in every theme
1405
 * - loads default JS scripts for use in every theme
1406
 *
1407
 * @param int $id_theme = 0
1408
 * @param bool $initialize = true
1409
 */
1410
function loadTheme($id_theme = 0, $initialize = true)
1411
{
1412 42
	global $user_info, $user_settings;
1413 42
	global $txt, $scripturl, $mbname, $modSettings;
1414 42
	global $context, $settings, $options;
1415
1416 42
	initTheme($id_theme);
1417
1418 42
	if (!$initialize)
1419 28
		return;
1420
1421 42
	loadThemeUrls();
1422
1423 42
	loadUserContext();
1424
1425
	// Set up some additional interface preference context
1426 42 View Code Duplication
	if (!empty($options['admin_preferences']))
1427 28
	{
1428
		$context['admin_preferences'] = serializeToJson($options['admin_preferences'], function ($array_form) {
1429
			global $context;
1430
1431
			$context['admin_preferences'] = $array_form;
1432
			require_once(SUBSDIR . '/Admin.subs.php');
1433
			updateAdminPreferences();
1434
		});
1435
	}
1436
	else
1437
	{
1438 42
		$context['admin_preferences'] = array();
1439
	}
1440
1441 42
	if (!$user_info['is_guest'])
1442 28
	{
1443 18 View Code Duplication
		if (!empty($options['minmax_preferences']))
1444 12
		{
1445
			$context['minmax_preferences'] = serializeToJson($options['minmax_preferences'], function ($array_form) {
1446
				global $settings, $user_info;
1447
1448
				// Update the option.
1449
				require_once(SUBSDIR . '/Themes.subs.php');
1450
				updateThemeOptions(array($settings['theme_id'], $user_info['id'], 'minmax_preferences', json_encode($array_form)));
1451
			});
1452
		}
1453
		else
1454
		{
1455 18
			$context['minmax_preferences'] = array();
1456
		}
1457 12
	}
1458
	// Guest may have collapsed the header, check the cookie to prevent collapse jumping
1459 24
	elseif ($user_info['is_guest'] && isset($_COOKIE['upshrink']))
1460
		$context['minmax_preferences'] = array('upshrink' => $_COOKIE['upshrink']);
1461
1462
	// @todo when are these set before loadTheme(0, true)?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1463 42
	loadThemeContext();
1464
1465
	// @todo These really don't belong in loadTheme() since they are more general than the theme.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
1466 42
	$context['session_var'] = $_SESSION['session_var'];
1467 42
	$context['session_id'] = $_SESSION['session_value'];
1468 42
	$context['forum_name'] = $mbname;
1469 42
	$context['forum_name_html_safe'] = $context['forum_name'];
1470 42
	$context['current_action'] = isset($_REQUEST['action']) ? $_REQUEST['action'] : null;
1471 42
	$context['current_subaction'] = isset($_REQUEST['sa']) ? $_REQUEST['sa'] : null;
1472
1473
	// Set some permission related settings.
1474 42
	if ($user_info['is_guest'] && !empty($modSettings['enableVBStyleLogin']))
1475 28
	{
1476 24
		$context['show_login_bar'] = true;
1477 24
		$context['theme_header_callbacks'][] = 'login_bar';
1478 24
		loadJavascriptFile('sha256.js', array('defer' => true));
1479 16
	}
1480
1481
	// This determines the server... not used in many places, except for login fixing.
1482 42
	detectServer();
1483
1484
	// Detect the browser. This is separated out because it's also used in attachment downloads
1485 42
	detectBrowser();
1486
1487
	// Set the top level linktree up.
1488 42
	array_unshift($context['linktree'], array(
1489 42
		'url' => $scripturl,
1490 42
		'name' => $context['forum_name']
1491 28
	));
1492
1493
	// Just some mobile-friendly settings
1494 42
	if ($context['browser_body_id'] == 'mobile')
1495 28
	{
1496
		// Disable the preview text.
1497
		$modSettings['message_index_preview'] = 0;
1498
		// Force the usage of click menu instead of a hover menu.
1499
		$options['use_click_menu'] = 1;
1500
		// No space left for a sidebar
1501
		$options['use_sidebar_menu'] = false;
1502
		// Disable the search dropdown.
1503
		$modSettings['search_dropdown'] = false;
1504
	}
1505
1506 42
	if (!isset($txt))
1507 28
		$txt = array();
1508
1509
	// Load the basic layers
1510 42
	theme()->loadDefaultLayers();
1511
1512
	// Defaults in case of odd things
1513 42
	$settings['avatars_on_indexes'] = 0;
1514
1515
	// Initialize the theme.
1516 42
	if (function_exists('template_init'))
1517 42
		$settings = array_merge($settings, template_init());
1518
1519
	// Call initialization theme integration functions.
1520 42
	call_integration_hook('integrate_init_theme', array($id_theme, &$settings));
1521
1522
	// Guests may still need a name.
1523 42 View Code Duplication
	if ($context['user']['is_guest'] && empty($context['user']['name']))
1524 28
		$context['user']['name'] = $txt['guest_title'];
1525
1526
	// Any theme-related strings that need to be loaded?
1527 42
	if (!empty($settings['require_theme_strings']))
1528 28
		loadLanguage('ThemeStrings', '', false);
1529
1530
	// Load font Awesome fonts, @deprecated in 1.1 and will be removed in 2.0
1531 42
	if (!empty($settings['require_font-awesome']) || !empty($modSettings['require_font-awesome']))
1532 28
	{
1533
		loadCSSFile('font-awesome.min.css');
1534
	}
1535
1536
	// We allow theme variants, because we're cool.
1537 42
	if (!empty($settings['theme_variants']))
1538 28
	{
1539 42
		theme()->loadThemeVariant();
1540 28
	}
1541
1542
	// A bit lonely maybe, though I think it should be set up *after* the theme variants detection
1543 42
	$context['header_logo_url_html_safe'] = empty($settings['header_logo_url']) ? $settings['images_url'] . '/' . $context['theme_variant_url'] . 'logo_elk.png' : Util::htmlspecialchars($settings['header_logo_url']);
1544
1545
	// Allow overriding the board wide time/number formats.
1546 42
	if (empty($user_settings['time_format']) && !empty($txt['time_format']))
1547 42
		$user_info['time_format'] = $txt['time_format'];
1548
1549 42 View Code Duplication
	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'always')
1550 28
	{
1551
		$settings['theme_url'] = $settings['default_theme_url'];
1552
		$settings['images_url'] = $settings['default_images_url'];
1553
		$settings['theme_dir'] = $settings['default_theme_dir'];
1554
	}
1555
1556
	// Make a special URL for the language.
1557 42
	$settings['lang_images_url'] = $settings['images_url'] . '/' . (!empty($txt['image_lang']) ? $txt['image_lang'] : $user_info['language']);
1558
1559
	// RTL languages require an additional stylesheet.
1560 42
	if ($context['right_to_left'])
1561 28
		loadCSSFile('rtl.css');
1562
1563 42 View Code Duplication
	if (!empty($context['theme_variant']) && $context['right_to_left'])
1564 28
		loadCSSFile($context['theme_variant'] . '/rtl' . $context['theme_variant'] . '.css');
1565
1566
	// This allows us to change the way things look for the admin.
1567 42
	$context['admin_features'] = isset($modSettings['admin_features']) ? explode(',', $modSettings['admin_features']) : array('cd,cp,k,w,rg,ml,pm');
1568
1569 42
	if (!empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged']))
1570 42
		$context['newsfeed_urls'] = array(
1571 42
			'rss' => $scripturl . '?action=.xml;type=rss2;limit=' . (!empty($modSettings['xmlnews_limit']) ? $modSettings['xmlnews_limit'] : 5),
1572 42
			'atom' => $scripturl . '?action=.xml;type=atom;limit=' . (!empty($modSettings['xmlnews_limit']) ? $modSettings['xmlnews_limit'] : 5)
1573 28
		);
1574
1575 42
	theme()->loadThemeJavascript();
1576
1577 42
	Hooks::get()->newPath(array('$themedir' => $settings['theme_dir']));
1578
1579
	// Any files to include at this point?
1580 42
	call_integration_include_hook('integrate_theme_include');
1581
1582
	// Call load theme integration functions.
1583 42
	call_integration_hook('integrate_load_theme');
1584
1585
	// We are ready to go.
1586 42
	$context['theme_loaded'] = true;
1587 42
}
1588
1589
/**
1590
 * Detects url and checks against expected boardurl
1591
 *
1592
 * Attempts to correct improper URL's
1593
 */
1594
function loadThemeUrls()
1595
{
1596 42
	global $scripturl, $boardurl, $modSettings;
1597
1598
	// Check to see if they're accessing it from the wrong place.
1599 42
	if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['SERVER_NAME']))
1600 28
	{
1601
		$detected_url = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https://' : 'http://';
1602
		$detected_url .= empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] . (empty($_SERVER['SERVER_PORT']) || $_SERVER['SERVER_PORT'] == '80' ? '' : ':' . $_SERVER['SERVER_PORT']) : $_SERVER['HTTP_HOST'];
1603
		$temp = preg_replace('~/' . basename($scripturl) . '(/.+)?$~', '', strtr(dirname($_SERVER['PHP_SELF']), '\\', '/'));
1604
		if ($temp != '/')
1605
			$detected_url .= $temp;
1606
	}
1607
1608 42
	if (isset($detected_url) && $detected_url != $boardurl)
1609 28
	{
1610
		// Try #1 - check if it's in a list of alias addresses.
1611
		if (!empty($modSettings['forum_alias_urls']))
1612
		{
1613
			$aliases = explode(',', $modSettings['forum_alias_urls']);
1614
			foreach ($aliases as $alias)
1615
			{
1616
				// Rip off all the boring parts, spaces, etc.
1617
				if ($detected_url == trim($alias) || strtr($detected_url, array('http://' => '', 'https://' => '')) == trim($alias))
1618
					$do_fix = true;
1619
			}
1620
		}
1621
1622
		// Hmm... check #2 - is it just different by a www?  Send them to the correct place!!
1623
		if (empty($do_fix) && strtr($detected_url, array('://' => '://www.')) == $boardurl && (empty($_GET) || count($_GET) == 1) && ELK != 'SSI')
1624
		{
1625
			// Okay, this seems weird, but we don't want an endless loop - this will make $_GET not empty ;).
1626
			if (empty($_GET))
1627
				redirectexit('wwwRedirect');
1628
			else
1629
			{
1630
				list ($k, $v) = each($_GET);
1631
				if ($k != 'wwwRedirect')
1632
					redirectexit('wwwRedirect;' . $k . '=' . $v);
1633
			}
1634
		}
1635
1636
		// #3 is just a check for SSL...
1637
		if (strtr($detected_url, array('https://' => 'http://')) == $boardurl)
1638
			$do_fix = true;
1639
1640
		// Okay, #4 - perhaps it's an IP address?  We're gonna want to use that one, then. (assuming it's the IP or something...)
1641
		if (!empty($do_fix) || preg_match('~^http[s]?://(?:[\d\.:]+|\[[\d:]+\](?::\d+)?)(?:$|/)~', $detected_url) == 1)
1642
		{
1643
			fixThemeUrls($detected_url);
1644
		}
1645
	}
1646 42
}
1647
1648
/**
1649
 * Loads various theme related settings into context and sets system wide theme defaults
1650
 */
1651
function loadThemeContext()
1652
{
1653 42
	global $context, $settings, $modSettings, $txt;
1654
1655
	// Some basic information...
1656
	$init = array(
1657 42
		'html_headers' => '',
1658 28
		'links' => array(),
1659 28
		'css_files' => array(),
1660 28
		'javascript_files' => array(),
1661 28
		'css_rules' => array(),
1662 28
		'javascript_inline' => array('standard' => array(), 'defer' => array()),
1663 28
		'javascript_vars' => array(),
1664 28
	);
1665 42
	foreach ($init as $area => $value)
1666
	{
1667 42
		$context[$area] = isset($context[$area]) ? $context[$area] : $value;
1668 28
	}
1669
1670
	// Set a couple of bits for the template.
1671 42
	$context['right_to_left'] = !empty($txt['lang_rtl']);
1672 42
	$context['tabindex'] = 1;
1673
1674 42
	$context['theme_variant'] = '';
1675 42
	$context['theme_variant_url'] = '';
1676
1677 42
	$context['menu_separator'] = !empty($settings['use_image_buttons']) ? ' ' : ' | ';
1678 42
	$context['can_register'] = empty($modSettings['registration_method']) || $modSettings['registration_method'] != 3;
1679
1680 42
	foreach (array('theme_header', 'upper_content') as $call)
1681
	{
1682 42
		if (!isset($context[$call . '_callbacks']))
1683 28
		{
1684 14
			$context[$call . '_callbacks'] = array();
1685
		}
1686 28
	}
1687
1688
	// This allows sticking some HTML on the page output - useful for controls.
1689 42
	$context['insert_after_template'] = '';
1690 42
}
1691
1692
/**
1693
 * Loads basic user information in to $context['user']
1694
 */
1695
function loadUserContext()
1696
{
1697 42
	global $context, $user_info, $txt, $modSettings;
1698
1699
	// Set up the contextual user array.
1700 42
	$context['user'] = array(
1701 42
		'id' => $user_info['id'],
1702 42
		'is_logged' => !$user_info['is_guest'],
1703 42
		'is_guest' => &$user_info['is_guest'],
1704 42
		'is_admin' => &$user_info['is_admin'],
1705 42
		'is_mod' => &$user_info['is_mod'],
1706 42
		'is_moderator' => &$user_info['is_moderator'],
1707
		// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
1708 42
		'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'])))),
1709 42
		'username' => $user_info['username'],
1710 42
		'language' => $user_info['language'],
1711 42
		'email' => $user_info['email'],
1712 42
		'ignoreusers' => $user_info['ignoreusers'],
1713
	);
1714
1715
	// Something for the guests
1716 42
	if (!$context['user']['is_guest'])
1717 28
	{
1718 18
		$context['user']['name'] = $user_info['name'];
1719 12
	}
1720 24 View Code Duplication
	elseif ($context['user']['is_guest'] && !empty($txt['guest_title']))
1721
	{
1722 24
		$context['user']['name'] = $txt['guest_title'];
1723 16
	}
1724
1725 42
	$context['user']['smiley_set'] = determineSmileySet($user_info['smiley_set'], $modSettings['smiley_sets_known']);
1726 42
	$context['smiley_enabled'] = $user_info['smiley_set'] !== 'none';
1727 42
	$context['user']['smiley_path'] = $modSettings['smileys_url'] . '/' . $context['user']['smiley_set'] . '/';
1728 42
}
1729
1730
/**
1731
 * Called if the detected URL is not the same as boardurl but is a common
1732
 * variation in which case it updates key system variables so it works.
1733
 *
1734
 * @param string $detected_url
1735
 */
1736
function fixThemeUrls($detected_url)
1737
{
1738
	global $boardurl, $scripturl, $settings, $modSettings, $context, $board_info;
1739
1740
	// Caching is good ;).
1741
	$oldurl = $boardurl;
1742
1743
	// Fix $boardurl and $scripturl.
1744
	$boardurl = $detected_url;
1745
	$scripturl = strtr($scripturl, array($oldurl => $boardurl));
1746
	$_SERVER['REQUEST_URL'] = strtr($_SERVER['REQUEST_URL'], array($oldurl => $boardurl));
1747
1748
	// Fix the theme urls...
1749
	$settings['theme_url'] = strtr($settings['theme_url'], array($oldurl => $boardurl));
1750
	$settings['default_theme_url'] = strtr($settings['default_theme_url'], array($oldurl => $boardurl));
1751
	$settings['actual_theme_url'] = strtr($settings['actual_theme_url'], array($oldurl => $boardurl));
1752
	$settings['images_url'] = strtr($settings['images_url'], array($oldurl => $boardurl));
1753
	$settings['default_images_url'] = strtr($settings['default_images_url'], array($oldurl => $boardurl));
1754
	$settings['actual_images_url'] = strtr($settings['actual_images_url'], array($oldurl => $boardurl));
1755
1756
	// And just a few mod settings :).
1757
	$modSettings['smileys_url'] = strtr($modSettings['smileys_url'], array($oldurl => $boardurl));
1758
	$modSettings['avatar_url'] = strtr($modSettings['avatar_url'], array($oldurl => $boardurl));
1759
1760
	// Clean up after loadBoard().
1761
	if (isset($board_info['moderators']))
1762
	{
1763
		foreach ($board_info['moderators'] as $k => $dummy)
1764
		{
1765
			$board_info['moderators'][$k]['href'] = strtr($dummy['href'], array($oldurl => $boardurl));
1766
			$board_info['moderators'][$k]['link'] = strtr($dummy['link'], array('"' . $oldurl => '"' . $boardurl));
1767
		}
1768
	}
1769
1770
	foreach ($context['linktree'] as $k => $dummy)
1771
		$context['linktree'][$k]['url'] = strtr($dummy['url'], array($oldurl => $boardurl));
1772
}
1773
1774
/**
1775
 * Determine the current user's smiley set
1776
 *
1777
 * @param mixed[] $user_smiley_set
1778
 * @param mixed[] $known_smiley_sets
1779
 *
1780
 * @return mixed
1781
 */
1782
function determineSmileySet($user_smiley_set, $known_smiley_sets)
1783
{
1784 42
	global $modSettings, $settings;
1785
1786 42
	if ((!in_array($user_smiley_set, explode(',', $known_smiley_sets)) && $user_smiley_set !== 'none') || empty($modSettings['smiley_sets_enable']))
1787 28
	{
1788 42
		$set = !empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default'];
1789 28
	}
1790
	else
1791
	{
1792
		$set = $user_smiley_set;
1793
	}
1794
1795 42
	return $set;
1796
}
1797
1798
/**
1799
 * This loads the bare minimum data.
1800
 *
1801
 * - Needed by scheduled tasks,
1802
 * - Needed by any other code that needs language files before the forum (the theme) is loaded.
1803
 */
1804
function loadEssentialThemeData()
1805
{
1806 7
	global $settings, $modSettings, $mbname, $context;
1807
1808 7
	$db = database();
1809
1810
	// Get all the default theme variables.
1811 7
	$db->fetchQueryCallback('
1812
		SELECT id_theme, variable, value
1813
		FROM {db_prefix}themes
1814
		WHERE id_member = {int:no_member}
1815 7
			AND id_theme IN (1, {int:theme_guests})',
1816
		array(
1817 7
			'no_member' => 0,
1818 7
			'theme_guests' => $modSettings['theme_guests'],
1819 4
		),
1820
		function ($row)
1821
		{
1822 7
			global $settings;
1823
1824 7
			$settings[$row['variable']] = $row['value'];
1825
1826
			// Is this the default theme?
1827 7
			if (in_array($row['variable'], array('theme_dir', 'theme_url', 'images_url')) && $row['id_theme'] == '1')
1828 7
				$settings['default_' . $row['variable']] = $row['value'];
1829 7
		}
1830 4
	);
1831
1832
	// Check we have some directories setup.
1833 7
	if (!Templates::getInstance()->hasDirectories())
1834 4
	{
1835
		Templates::getInstance()->reloadDirectories($settings);
1836
	}
1837
1838
	// Assume we want this.
1839 7
	$context['forum_name'] = $mbname;
1840 7
	$context['forum_name_html_safe'] = $context['forum_name'];
1841
1842 7
	loadLanguage('index+Addons');
1843 7
}
1844
1845
/**
1846
 * Load a template - if the theme doesn't include it, use the default.
1847
 *
1848
 * What it does:
1849
 *
1850
 * - loads a template file with the name template_name from the current, default, or base theme.
1851
 * - detects a wrong default theme directory and tries to work around it.
1852
 * - can be used to only load style sheets by using false as the template name
1853
 *   loading of style sheets with this function is deprecated, use loadCSSFile instead
1854
 * - if $settings['template_dirs'] is empty, it delays the loading of the template
1855
 *
1856
 * @uses the requireTemplate() function to actually load the file.
1857
 * @param string|false $template_name
1858
 * @param string[]|string $style_sheets any style sheets to load with the template
1859
 * @param bool $fatal = true if fatal is true, dies with an error message if the template cannot be found
1860
 *
1861
 * @return boolean|null
1862
 * @throws Elk_Exception
1863
 */
1864
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
1865
{
1866 78
	return Templates::getInstance()->load($template_name, $style_sheets, $fatal);
1867
}
1868
1869
/**
1870
 * Load a sub-template.
1871
 *
1872
 * What it does:
1873
 *
1874
 * - loads the sub template specified by sub_template_name, which must be in an already-loaded template.
1875
 * - if ?debug is in the query string, shows administrators a marker after every sub template
1876
 * for debugging purposes.
1877
 *
1878
 * @param string $sub_template_name
1879
 * @param bool|string $fatal = false
1880
 * - $fatal = true is for templates that shouldn't get a 'pretty' error screen
1881
 * - $fatal = 'ignore' to skip
1882
 * @throws Elk_Exception
1883
 */
1884
function loadSubTemplate($sub_template_name, $fatal = false)
1885
{
1886
	Templates::getInstance()->loadSubTemplate($sub_template_name, $fatal);
1887
1888
	return true;
1889
}
1890
1891
/**
1892
 * Add a CSS file for output later
1893
 *
1894
 * @param string[]|string $filenames string or array of filenames to work on
1895
 * @param mixed[] $params = array()
1896
 * Keys are the following:
1897
 * - ['local'] (true/false): define if the file is local
1898
 * - ['fallback'] (true/false): if false  will attempt to load the file
1899
 *   from the default theme if not found in the current theme
1900
 * - ['stale'] (true/false/string): if true or null, use cache stale,
1901
 *   false do not, or used a supplied string
1902
 * @param string $id optional id to use in html id=""
1903
 */
1904
function loadCSSFile($filenames, $params = array(), $id = '')
1905
{
1906 42
	global $context;
1907
1908 42
	if (empty($filenames))
1909 28
		return;
1910
1911 42
	if (!is_array($filenames))
1912 42
		$filenames = array($filenames);
1913
1914 42
	if (in_array('admin.css', $filenames))
1915 28
		$filenames[] = $context['theme_variant'] . '/admin' . $context['theme_variant'] . '.css';
1916
1917 42
	$params['subdir'] = 'css';
1918 42
	$params['extension'] = 'css';
1919 42
	$params['index_name'] = 'css_files';
1920 42
	$params['debug_index'] = 'sheets';
1921
1922 42
	loadAssetFile($filenames, $params, $id);
1923 42
}
1924
1925
/**
1926
 * Add a Javascript file for output later
1927
 *
1928
 * What it does:
1929
 *
1930
 * - Can be passed an array of filenames, all which will have the same
1931
 *   parameters applied,
1932
 * - if you need specific parameters on a per file basis, call it multiple times
1933
 *
1934
 * @param string[]|string $filenames string or array of filenames to work on
1935
 * @param mixed[] $params = array()
1936
 * Keys are the following:
1937
 * - ['local'] (true/false): define if the file is local, if file does not
1938
 *     start with http its assumed local
1939
 * - ['defer'] (true/false): define if the file should load in <head> or before
1940
 *     the closing <html> tag
1941
 * - ['fallback'] (true/false): if true will attempt to load the file from the
1942
 *     default theme if not found in the current this is the default behavior
1943
 *     if this is not supplied
1944
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
1945
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
1946
 *     not, or used a supplied string
1947
 * @param string $id = '' optional id to use in html id=""
1948
 */
1949
function loadJavascriptFile($filenames, $params = array(), $id = '')
1950
{
1951 48
	if (empty($filenames))
1952 32
		return;
1953
1954 48
	$params['subdir'] = 'scripts';
1955 48
	$params['extension'] = 'js';
1956 48
	$params['index_name'] = 'js_files';
1957 48
	$params['debug_index'] = 'javascript';
1958
1959 48
	loadAssetFile($filenames, $params, $id);
1960 48
}
1961
1962
/**
1963
 * Add an asset (css, js or other) file for output later
1964
 *
1965
 * What it does:
1966
 *
1967
 * - Can be passed an array of filenames, all which will have the same
1968
 *   parameters applied,
1969
 * - If you need specific parameters on a per file basis, call it multiple times
1970
 *
1971
 * @param string[]|string $filenames string or array of filenames to work on
1972
 * @param mixed[] $params = array()
1973
 * Keys are the following:
1974
 * - ['subdir'] (string): the subdirectory of the theme dir the file is in
1975
 * - ['extension'] (string): the extension of the file (e.g. css)
1976
 * - ['index_name'] (string): the $context index that holds the array of loaded
1977
 *     files
1978
 * - ['debug_index'] (string): the index that holds the array of loaded
1979
 *     files for debugging debug
1980
 * - ['local'] (true/false): define if the file is local, if file does not
1981
 *     start with http or // (schema-less URLs) its assumed local.
1982
 *     The parameter is in fact useful only for files whose name starts with
1983
 *     http, in any other case (e.g. passing a local URL) the parameter would
1984
 *     fail in properly adding the file to the list.
1985
 * - ['defer'] (true/false): define if the file should load in <head> or before
1986
 *     the closing <html> tag
1987
 * - ['fallback'] (true/false): if true will attempt to load the file from the
1988
 *     default theme if not found in the current this is the default behavior
1989
 *     if this is not supplied
1990
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
1991
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
1992
 *     not, or used a supplied string
1993
 * @param string $id = '' optional id to use in html id=""
1994
 */
1995
function loadAssetFile($filenames, $params = array(), $id = '')
1996
{
1997 48
	global $settings, $context, $db_show_debug;
1998
1999 48
	if (empty($filenames))
2000 32
		return;
2001
2002 48
	$cache = Cache::instance();
2003
2004 48
	if (!is_array($filenames))
2005 48
		$filenames = array($filenames);
2006
2007
	// Static values for all these settings
2008 48
	if (!isset($params['stale']) || $params['stale'] === true)
2009 48
		$staler_string = CACHE_STALE;
2010
	elseif (is_string($params['stale']))
2011
		$staler_string = ($params['stale'][0] === '?' ? $params['stale'] : '?' . $params['stale']);
2012
	else
2013
		$staler_string = '';
2014
2015 48
	$fallback = (!empty($params['fallback']) && ($params['fallback'] === false)) ? false : true;
2016 48
	$dir = '/' . $params['subdir'] . '/';
2017
2018
	// Whoa ... we've done this before yes?
2019 48
	$cache_name = 'load_' . $params['extension'] . '_' . hash('md5', $settings['theme_dir'] . implode('_', $filenames));
2020 48
	$temp = array();
2021 48
	if ($cache->getVar($temp, $cache_name, 600))
2022 32
	{
2023
		if (empty($context[$params['index_name']]))
2024
			$context[$params['index_name']] = array();
2025
2026
		$context[$params['index_name']] += $temp;
2027
2028
		if ($db_show_debug === true)
2029
		{
2030
			foreach ($temp as $temp_params)
2031
			{
2032
				Debug::get()->add($params['debug_index'], $temp_params['options']['basename'] . '(' . (!empty($temp_params['options']['local']) ? (!empty($temp_params['options']['url']) ? basename($temp_params['options']['url']) : basename($temp_params['options']['dir'])) : '') . ')');
2033
			}
2034
		}
2035
	}
2036
	else
2037
	{
2038 48
		$this_build = array();
2039
2040
		// All the files in this group use the above parameters
2041 48
		foreach ($filenames as $filename)
2042
		{
2043
			// Account for shorthand like admin.ext?xyz11 filenames
2044 48
			$has_cache_staler = strpos($filename, '.' . $params['extension'] . '?');
2045 16
			if ($has_cache_staler)
2046 32
			{
2047
				$cache_staler = $staler_string;
2048
				$params['basename'] = substr($filename, 0, $has_cache_staler + strlen($params['extension']) + 1);
2049
			}
2050
			else
2051
			{
2052 48
				$cache_staler = '';
2053 48
				$params['basename'] = $filename;
2054
			}
2055 48
			$this_id = empty($id) ? strtr(basename($filename), '?', '_') : $id;
2056
2057
			// Is this a local file?
2058 48
			if (!empty($params['local']) || (substr($filename, 0, 4) !== 'http' && substr($filename, 0, 2) !== '//'))
2059 32
			{
2060 48
				$params['local'] = true;
2061 48
				$params['dir'] = $settings['theme_dir'] . $dir;
2062 48
				$params['url'] = $settings['theme_url'];
2063
2064
				// Fallback if we are not already in the default theme
2065 48
				if ($fallback && ($settings['theme_dir'] !== $settings['default_theme_dir']) && !file_exists($settings['theme_dir'] . $dir . $params['basename']))
2066 32
				{
2067
					// Can't find it in this theme, how about the default?
2068
					if (file_exists($settings['default_theme_dir'] . $dir . $params['basename']))
2069
					{
2070
						$filename = $settings['default_theme_url'] . $dir . $params['basename'] . $cache_staler;
2071
						$params['dir'] = $settings['default_theme_dir'] . $dir;
2072
						$params['url'] = $settings['default_theme_url'];
2073
					}
2074
					else
2075
						$filename = false;
2076
				}
2077
				else
2078 48
					$filename = $settings['theme_url'] . $dir . $params['basename'] . $cache_staler;
2079 32
			}
2080
2081
			// Add it to the array for use in the template
2082 48
			if (!empty($filename))
2083 32
			{
2084 48
				$this_build[$this_id] = $context[$params['index_name']][$this_id] = array('filename' => $filename, 'options' => $params);
2085
2086 48
				if ($db_show_debug === true)
2087 32
					Debug::get()->add($params['debug_index'], $params['basename'] . '(' . (!empty($params['local']) ? (!empty($params['url']) ? basename($params['url']) : basename($params['dir'])) : '') . ')');
2088 32
			}
2089
2090
			// Save it so we don't have to build this so often
2091 48
			$cache->put($cache_name, $this_build, 600);
2092 32
		}
2093
	}
2094 48
}
2095
2096
/**
2097
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
2098
 *
2099
 * @param mixed[] $vars array of vars to include in the output done as 'varname' => 'var value'
2100
 * @param bool $escape = false, whether or not to escape the value
2101
 */
2102
function addJavascriptVar($vars, $escape = false)
2103
{
2104 12
	theme()->addJavascriptVar($vars, $escape);
2105 12
}
2106
2107
/**
2108
 * Add a block of inline Javascript code to be executed later
2109
 *
2110
 * What it does:
2111
 *
2112
 * - only use this if you have to, generally external JS files are better, but for very small scripts
2113
 *   or for scripts that require help from PHP/whatever, this can be useful.
2114
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
2115
 *
2116
 * @param string $javascript
2117
 * @param bool $defer = false, define if the script should load in <head> or before the closing <html> tag
2118
 */
2119
function addInlineJavascript($javascript, $defer = false)
2120
{
2121 24
	theme()->addInlineJavascript($javascript, $defer);
2122 24
}
2123
2124
/**
2125
 * Load a language file.
2126
 *
2127
 * - Tries the current and default themes as well as the user and global languages.
2128
 *
2129
 * @param string $template_name
2130
 * @param string $lang = ''
2131
 * @param bool $fatal = true
2132
 * @param bool $force_reload = false
2133
 * @return string The language actually loaded.
2134
 */
2135
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
2136
{
2137 224
	global $user_info, $language, $settings, $modSettings;
2138 224
	global $db_show_debug, $txt;
2139 224
	static $already_loaded = array();
2140
2141
	// Default to the user's language.
2142 224
	if ($lang == '')
2143 220
		$lang = isset($user_info['language']) ? $user_info['language'] : $language;
2144
2145 224
	if (!$force_reload && isset($already_loaded[$template_name]) && $already_loaded[$template_name] == $lang)
2146 214
		return $lang;
2147
2148
	// Do we want the English version of language file as fallback?
2149 102
	if (empty($modSettings['disable_language_fallback']) && $lang != 'english')
2150 68
		loadLanguage($template_name, 'english', false);
2151
2152
	// Make sure we have $settings - if not we're in trouble and need to find it!
2153 102
	if (empty($settings['default_theme_dir']))
2154 68
		loadEssentialThemeData();
2155
2156
	// What theme are we in?
2157 102
	$theme_name = basename($settings['theme_url']);
2158 102
	if (empty($theme_name))
2159 68
		$theme_name = 'unknown';
2160
2161 102
	$fix_arrays = false;
2162
	// For each file open it up and write it out!
2163 102
	foreach (explode('+', $template_name) as $template)
2164
	{
2165 102
		if ($template === 'index')
2166 68
			$fix_arrays = true;
2167
2168
		// Obviously, the current theme is most important to check.
2169
		$attempts = array(
2170 102
			array($settings['theme_dir'], $template, $lang, $settings['theme_url']),
2171 102
			array($settings['theme_dir'], $template, $language, $settings['theme_url']),
2172 68
		);
2173
2174
		// Do we have a base theme to worry about?
2175 102
		if (isset($settings['base_theme_dir']))
2176 68
		{
2177
			$attempts[] = array($settings['base_theme_dir'], $template, $lang, $settings['base_theme_url']);
2178
			$attempts[] = array($settings['base_theme_dir'], $template, $language, $settings['base_theme_url']);
2179
		}
2180
2181
		// Fall back on the default theme if necessary.
2182 102
		$attempts[] = array($settings['default_theme_dir'], $template, $lang, $settings['default_theme_url']);
2183 102
		$attempts[] = array($settings['default_theme_dir'], $template, $language, $settings['default_theme_url']);
2184
2185
		// Fall back on the English language if none of the preferred languages can be found.
2186 102
		if (!in_array('english', array($lang, $language)))
2187 68
		{
2188
			$attempts[] = array($settings['theme_dir'], $template, 'english', $settings['theme_url']);
2189
			$attempts[] = array($settings['default_theme_dir'], $template, 'english', $settings['default_theme_url']);
2190
		}
2191
2192 102
		$templates = Templates::getInstance();
2193
2194
		// Try to find the language file.
2195 102
		$found = false;
2196 102
		foreach ($attempts as $k => $file)
2197
		{
2198 102
			if (file_exists($file[0] . '/languages/' . $file[2] . '/' . $file[1] . '.' . $file[2] . '.php'))
2199 68
			{
2200
				// Include it!
2201 102
				$templates->templateInclude($file[0] . '/languages/' . $file[2] . '/' . $file[1] . '.' . $file[2] . '.php');
2202
2203
				// Note that we found it.
2204 102
				$found = true;
2205
2206 102
				break;
2207
			}
2208
			// @deprecated since 1.0 - old way of archiving language files, all in one directory
2209
			elseif (file_exists($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php'))
2210
			{
2211
				// Include it!
2212
				$templates->templateInclude($file[0] . '/languages/' . $file[1] . '.' . $file[2] . '.php');
2213
2214
				// Note that we found it.
2215
				$found = true;
2216
2217
				break;
2218
			}
2219 68
		}
2220
2221
		// That couldn't be found!  Log the error, but *try* to continue normally.
2222 102
		if (!$found && $fatal)
2223 68
		{
2224
			Errors::instance()->log_error(sprintf($txt['theme_language_error'], $template_name . '.' . $lang, 'template'));
2225 34
			break;
2226
		}
2227 68
	}
2228
2229 34
	if ($fix_arrays)
2230 68
		fix_calendar_text();
2231
2232
	// Keep track of what we're up to soldier.
2233 102
	if ($db_show_debug === true)
2234 68
		Debug::get()->add('language_files', $template_name . '.' . $lang . ' (' . $theme_name . ')');
2235
2236
	// Remember what we have loaded, and in which language.
2237 102
	$already_loaded[$template_name] = $lang;
2238
2239
	// Return the language actually loaded.
2240 102
	return $lang;
2241
}
2242
2243
/**
2244
 * Loads / Sets arrays for use in date display
2245
 */
2246
function fix_calendar_text()
2247
{
2248 1
	global $txt;
2249
2250 1
	$txt['days'] = array(
2251 1
		$txt['sunday'],
2252 1
		$txt['monday'],
2253 1
		$txt['tuesday'],
2254 1
		$txt['wednesday'],
2255 1
		$txt['thursday'],
2256 1
		$txt['friday'],
2257 1
		$txt['saturday'],
2258
	);
2259 1
	$txt['days_short'] = array(
2260 1
		$txt['sunday_short'],
2261 1
		$txt['monday_short'],
2262 1
		$txt['tuesday_short'],
2263 1
		$txt['wednesday_short'],
2264 1
		$txt['thursday_short'],
2265 1
		$txt['friday_short'],
2266 1
		$txt['saturday_short'],
2267
	);
2268 1
	$txt['months'] = array(
2269 1
		1 => $txt['january'],
2270 1
		$txt['february'],
2271 1
		$txt['march'],
2272 1
		$txt['april'],
2273 1
		$txt['may'],
2274 1
		$txt['june'],
2275 1
		$txt['july'],
2276 1
		$txt['august'],
2277 1
		$txt['september'],
2278 1
		$txt['october'],
2279 1
		$txt['november'],
2280 1
		$txt['december'],
2281
	);
2282 1
	$txt['months_titles'] = array(
2283 1
		1 => $txt['january_titles'],
2284 1
		$txt['february_titles'],
2285 1
		$txt['march_titles'],
2286 1
		$txt['april_titles'],
2287 1
		$txt['may_titles'],
2288 1
		$txt['june_titles'],
2289 1
		$txt['july_titles'],
2290 1
		$txt['august_titles'],
2291 1
		$txt['september_titles'],
2292 1
		$txt['october_titles'],
2293 1
		$txt['november_titles'],
2294 1
		$txt['december_titles'],
2295
	);
2296 1
	$txt['months_short'] = array(
2297 1
		1 => $txt['january_short'],
2298 1
		$txt['february_short'],
2299 1
		$txt['march_short'],
2300 1
		$txt['april_short'],
2301 1
		$txt['may_short'],
2302 1
		$txt['june_short'],
2303 1
		$txt['july_short'],
2304 1
		$txt['august_short'],
2305 1
		$txt['september_short'],
2306 1
		$txt['october_short'],
2307 1
		$txt['november_short'],
2308 1
		$txt['december_short'],
2309
	);
2310 1
}
2311
2312
/**
2313
 * Get all parent boards (requires first parent as parameter)
2314
 *
2315
 * What it does:
2316
 *
2317
 * - It finds all the parents of id_parent, and that board itself.
2318
 * - Additionally, it detects the moderators of said boards.
2319
 * - Returns an array of information about the boards found.
2320
 *
2321
 * @param int $id_parent
2322
 *
2323
 * @return array
2324
 * @throws Elk_Exception parent_not_found
2325
 */
2326
function getBoardParents($id_parent)
2327
{
2328 66
	global $scripturl;
2329
2330 66
	$db = database();
2331 66
	$cache = Cache::instance();
2332 66
	$boards = array();
2333
2334
	// First check if we have this cached already.
2335 66
	if (!$cache->getVar($boards, 'board_parents-' . $id_parent, 480))
2336 44
	{
2337 66
		$boards = array();
2338 66
		$original_parent = $id_parent;
2339
2340
		// Loop while the parent is non-zero.
2341 66
		while ($id_parent != 0)
2342
		{
2343 60
			$result = $db->query('', '
2344
				SELECT
2345
					b.id_parent, b.name, {int:board_parent} AS id_board, COALESCE(mem.id_member, 0) AS id_moderator,
2346
					mem.real_name, b.child_level
2347
				FROM {db_prefix}boards AS b
2348
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
2349
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
2350 60
				WHERE b.id_board = {int:board_parent}',
2351
				array(
2352 60
					'board_parent' => $id_parent,
2353
				)
2354 40
			);
2355
			// In the EXTREMELY unlikely event this happens, give an error message.
2356 60
			if ($db->num_rows($result) == 0)
2357 40
			{
2358
				throw new Elk_Exception('parent_not_found', 'critical');
2359
			}
2360 60
			while ($row = $db->fetch_assoc($result))
2361
			{
2362 60
				if (!isset($boards[$row['id_board']]))
2363 40
				{
2364 60
					$id_parent = $row['id_parent'];
2365 60
					$boards[$row['id_board']] = array(
2366 60
						'url' => $scripturl . '?board=' . $row['id_board'] . '.0',
2367 60
						'name' => $row['name'],
2368 60
						'level' => $row['child_level'],
2369 40
						'moderators' => array()
2370 40
					);
2371 40
				}
2372
2373
				// If a moderator exists for this board, add that moderator for all children too.
2374 60
				if (!empty($row['id_moderator']))
2375 40
					foreach ($boards as $id => $dummy)
2376
					{
2377
						$boards[$id]['moderators'][$row['id_moderator']] = array(
2378
							'id' => $row['id_moderator'],
2379
							'name' => $row['real_name'],
2380
							'href' => $scripturl . '?action=profile;u=' . $row['id_moderator'],
2381
							'link' => '<a href="' . $scripturl . '?action=profile;u=' . $row['id_moderator'] . '">' . $row['real_name'] . '</a>'
2382
						);
2383
					}
2384 40
			}
2385 60
			$db->free_result($result);
2386 40
		}
2387
2388 66
		$cache->put('board_parents-' . $original_parent, $boards, 480);
2389 44
	}
2390
2391 66
	return $boards;
2392
}
2393
2394
/**
2395
 * Attempt to reload our known languages.
2396
 *
2397
 * @param bool $use_cache = true
2398
 */
2399
function getLanguages($use_cache = true)
2400
{
2401 6
	global $settings;
2402
2403 6
	$cache = Cache::instance();
2404
2405
	// Either we don't use the cache, or its expired.
2406 6
	$languages = array();
2407
2408 6
	if (!$use_cache || !$cache->getVar($languages, 'known_languages', $cache->levelLowerThan(2) ? 86400 : 3600))
2409 4
	{
2410
		// If we don't have our theme information yet, lets get it.
2411 6
		if (empty($settings['default_theme_dir']))
2412 4
			loadTheme(0, false);
2413
2414
		// Default language directories to try.
2415
		$language_directories = array(
2416 6
			$settings['default_theme_dir'] . '/languages',
2417 6
			$settings['actual_theme_dir'] . '/languages',
2418 4
		);
2419
2420
		// We possibly have a base theme directory.
2421 6
		if (!empty($settings['base_theme_dir']))
2422 4
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
2423
2424
		// Remove any duplicates.
2425 6
		$language_directories = array_unique($language_directories);
2426
2427 6
		foreach ($language_directories as $language_dir)
2428
		{
2429
			// Can't look in here... doesn't exist!
2430 6
			if (!file_exists($language_dir))
2431 4
				continue;
2432
2433 6
			$dir = dir($language_dir);
2434 6
			while ($entry = $dir->read())
2435
			{
2436
				// Only directories are interesting
2437 6
				if ($entry == '..' || !is_dir($dir->path . '/' . $entry))
0 ignored issues
show
Bug introduced by
The property path does not seem to exist in Directory.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
2438 6
					continue;
2439
2440
				// @todo at some point we may want to simplify that stuff (I mean scanning all the files just for index)
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
2441 6
				$file_dir = dir($dir->path . '/' . $entry);
2442 6
				while ($file_entry = $file_dir->read())
2443
				{
2444
					// Look for the index language file....
2445 6
					if (!preg_match('~^index\.(.+)\.php$~', $file_entry, $matches))
2446 6
						continue;
2447
2448 6
					$languages[$matches[1]] = array(
2449 6
						'name' => Util::ucwords(strtr($matches[1], array('_' => ' '))),
2450 4
						'selected' => false,
2451 6
						'filename' => $matches[1],
2452 6
						'location' => $language_dir . '/' . $entry . '/index.' . $matches[1] . '.php',
2453
					);
2454 4
				}
2455 6
				$file_dir->close();
2456 4
			}
2457 6
			$dir->close();
2458 4
		}
2459
2460
		// Lets cash in on this deal.
2461 6
		$cache->put('known_languages', $languages, $cache->isEnabled() && $cache->levelLowerThan(1) ? 86400 : 3600);
2462 4
	}
2463
2464 6
	return $languages;
2465
}
2466
2467
/**
2468
 * Initialize a database connection.
2469
 */
2470
function loadDatabase()
2471
{
2472
	global $db_persist, $db_server, $db_user, $db_passwd, $db_port;
2473
	global $db_type, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix;
2474
2475
	// Database stuffs
2476
	require_once(SOURCEDIR . '/database/Database.subs.php');
2477
2478
	// Figure out what type of database we are using.
2479
	if (empty($db_type) || !file_exists(SOURCEDIR . '/database/Db-' . $db_type . '.class.php'))
2480
		$db_type = 'mysql';
2481
2482
	// 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.
2483
	if (ELK === 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
2484
		$connection = elk_db_initiate($db_server, $db_name, $ssi_db_user, $ssi_db_passwd, $db_prefix, array('persist' => $db_persist, 'non_fatal' => true, 'dont_select_db' => true, 'port' => $db_port), $db_type);
2485
2486
	// Either we aren't in SSI mode, or it failed.
2487
	if (empty($connection))
2488
		$connection = elk_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, array('persist' => $db_persist, 'dont_select_db' => ELK === 'SSI', 'port' => $db_port), $db_type);
2489
2490
	// Safe guard here, if there isn't a valid connection lets put a stop to it.
2491
	if (!$connection)
2492
		Errors::instance()->display_db_error();
2493
2494
	// If in SSI mode fix up the prefix.
2495
	$db = database();
2496
	if (ELK === 'SSI')
2497
		$db_prefix = $db->fix_prefix($db_prefix, $db_name);
2498
2499
	// Case sensitive database? Let's define a constant.
2500
	if ($db->db_case_sensitive() && !defined('DB_CASE_SENSITIVE'))
2501
		DEFINE('DB_CASE_SENSITIVE', '1');
2502
}
2503
2504
/**
2505
 * Determine the user's avatar type and return the information as an array
2506
 *
2507
 * @todo this function seems more useful than expected, it should be improved. :P
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
2508
 *
2509
 * @param mixed[] $profile array containing the users profile data
2510
 * @return mixed[] $avatar
2511
 */
2512
function determineAvatar($profile)
2513
{
2514 12
	global $modSettings, $scripturl, $settings;
2515
2516 12
	if (empty($profile))
2517 8
		return array();
2518
2519 12
	$avatar_protocol = substr(strtolower($profile['avatar']), 0, 7);
2520
2521
	// uploaded avatar?
2522 12
	if ($profile['id_attach'] > 0 && empty($profile['avatar']))
2523 8
	{
2524
		// where are those pesky avatars?
2525
		$avatar_url = empty($profile['attachment_type']) ? $scripturl . '?action=dlattach;attach=' . $profile['id_attach'] . ';type=avatar' : $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
2526
2527
		$avatar = array(
2528
			'name' => $profile['avatar'],
2529
			'image' => '<img class="avatar avatarresize" src="' . $avatar_url . '" alt="" />',
2530
			'href' => $avatar_url,
2531
			'url' => '',
2532
		);
2533
	}
2534
	// remote avatar?
2535 12
	elseif ($avatar_protocol === 'http://' || $avatar_protocol === 'https:/')
2536
	{
2537
		$avatar = array(
2538
			'name' => $profile['avatar'],
2539
			'image' => '<img class="avatar avatarresize" src="' . $profile['avatar'] . '" alt="" />',
2540
			'href' => $profile['avatar'],
2541
			'url' => $profile['avatar'],
2542
		);
2543
	}
2544
	// Gravatar instead?
2545 12
	elseif (!empty($profile['avatar']) && $profile['avatar'] === 'gravatar')
2546
	{
2547
		// Gravatars URL.
2548
		$gravatar_url = '//www.gravatar.com/avatar/' . hash('md5', strtolower($profile['email_address'])) . '?s=' . $modSettings['avatar_max_height'] . (!empty($modSettings['gravatar_rating']) ? ('&amp;r=' . $modSettings['gravatar_rating']) : '');
2549
2550
		$avatar = array(
2551
			'name' => $profile['avatar'],
2552
			'image' => '<img class="avatar avatarresize" src="' . $gravatar_url . '" alt="" />',
2553
			'href' => $gravatar_url,
2554
			'url' => $gravatar_url,
2555
		);
2556
	}
2557
	// an avatar from the gallery?
2558 12
	elseif (!empty($profile['avatar']) && !($avatar_protocol === 'http://' || $avatar_protocol === 'https:/'))
2559
	{
2560
		$avatar = array(
2561
			'name' => $profile['avatar'],
2562
			'image' => '<img class="avatar avatarresize" src="' . $modSettings['avatar_url'] . '/' . $profile['avatar'] . '" alt="" />',
2563
			'href' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
2564
			'url' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
2565
		);
2566
	}
2567
	// no custom avatar found yet, maybe a default avatar?
2568 12
	elseif (!empty($modSettings['avatar_default']) && empty($profile['avatar']) && empty($profile['filename']))
2569
	{
2570
		// $settings not initialized? We can't do anything further..
2571
		if (!empty($settings))
2572
		{
2573
			// Let's proceed with the default avatar.
2574
			// TODO: This should be incorporated into the theme.
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
2575
			$avatar = array(
2576
				'name' => '',
2577
				'image' => '<img class="avatar avatarresize" src="' . $settings['images_url'] . '/default_avatar.png" alt="" />',
2578
				'href' => $settings['images_url'] . '/default_avatar.png',
2579
				'url' => 'http://',
2580
			);
2581
		}
2582
		else
2583
		{
2584
			$avatar = array();
2585
		}
2586
	}
2587
	// finally ...
2588
	else
2589
		$avatar = array(
2590 12
			'name' => '',
2591 8
			'image' => '',
2592 8
			'href' => '',
2593
			'url' => ''
2594 8
		);
2595
2596
	// Make sure there's a preview for gravatars available.
2597 12
	$avatar['gravatar_preview'] = '//www.gravatar.com/avatar/' . hash('md5', strtolower($profile['email_address'])) . '?s=' . $modSettings['avatar_max_height'] . (!empty($modSettings['gravatar_rating']) ? ('&amp;r=' . $modSettings['gravatar_rating']) : '');
2598
2599 12
	call_integration_hook('integrate_avatar', array(&$avatar, $profile));
2600
2601 12
	return $avatar;
2602
}
2603
2604
/**
2605
 * Get information about the server
2606
 */
2607
function detectServer()
2608
{
2609 91
	global $context;
2610 91
	static $server = null;
2611
2612 91
	if ($server === null)
2613 61
	{
2614
		$server = new Server($_SERVER);
2615
		$servers = array('iis', 'apache', 'litespeed', 'lighttpd', 'nginx', 'cgi', 'windows');
2616
		$context['server'] = array();
2617
		foreach ($servers as $name)
2618
		{
2619
			$context['server']['is_' . $name] = $server->is($name);
2620
		}
2621
2622
		$context['server']['iso_case_folding'] = $server->is('iso_case_folding');
2623
		// A bug in some versions of IIS under CGI (older ones) makes cookie setting not work with Location: headers.
2624
		$context['server']['needs_login_fix'] = $server->is('needs_login_fix');
2625
	}
2626
2627 91
	return $server;
2628
}
2629
2630
/**
2631
 * Returns if a webserver is of type server (apache, nginx, etc)
2632
 *
2633
 * @param $server
2634
 *
2635
 * @return bool
2636
 */
2637
function serverIs($server)
2638
{
2639
	return detectServer()->is($server);
2640
}
2641
2642
/**
2643
 * Do some important security checks:
2644
 *
2645
 * What it does:
2646
 *
2647
 * - Checks the existence of critical files e.g. install.php
2648
 * - Checks for an active admin session.
2649
 * - Checks cache directory is writable.
2650
 * - Calls secureDirectory to protect attachments & cache.
2651
 * - Checks if the forum is in maintenance mode.
2652
 */
2653
function doSecurityChecks()
2654
{
2655
	global $modSettings, $context, $maintenance, $user_info, $txt, $scripturl, $user_settings, $options;
2656
2657
	$show_warnings = false;
2658
2659
	$cache = Cache::instance();
2660
2661
	if (allowedTo('admin_forum') && !$user_info['is_guest'])
2662
	{
2663
		// If agreement is enabled, at least the english version shall exists
2664 View Code Duplication
		if ($modSettings['requireAgreement'] && !file_exists(BOARDDIR . '/agreement.txt'))
2665
		{
2666
			$context['security_controls_files']['title'] = $txt['generic_warning'];
2667
			$context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing'];
2668
			$show_warnings = true;
2669
		}
2670
2671
		// Cache directory writable?
2672 View Code Duplication
		if ($cache->isEnabled() && !is_writable(CACHEDIR))
2673
		{
2674
			$context['security_controls_files']['title'] = $txt['generic_warning'];
2675
			$context['security_controls_files']['errors']['cache'] = $txt['cache_writable'];
2676
			$show_warnings = true;
2677
		}
2678
2679
		if (checkSecurityFiles())
2680
			$show_warnings = true;
2681
2682
		// We are already checking so many files...just few more doesn't make any difference! :P
2683
		require_once(SUBSDIR . '/Attachments.subs.php');
2684
		$path = getAttachmentPath();
2685
		secureDirectory($path, true);
2686
		secureDirectory(CACHEDIR, false, '"\.(js|css)$"');
2687
2688
		// Active admin session?
2689
		if (isAdminSessionActive())
2690
			$context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], ($scripturl . '?action=admin;area=adminlogoff;redir;' . $context['session_var'] . '=' . $context['session_id']));
2691
2692
		// Maintenance mode enabled?
2693
		if (!empty($maintenance))
2694
			$context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], ($scripturl . '?action=admin;area=serversettings;' . $context['session_var'] . '=' . $context['session_id']));
2695
2696
		// New updates
2697
		if (defined('FORUM_VERSION'))
2698
		{
2699
			$index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION);
2700
			if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index]))
2701
			{
2702
				$show_warnings = true;
2703
				$context['new_version_updates'] = array(
2704
					'title' => $txt['new_version_updates'],
2705
					'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text'])),
2706
				);
2707
			}
2708
		}
2709
	}
2710
2711
	// Check for database errors.
2712
	if (!empty($_SESSION['query_command_denied']))
2713
	{
2714
		if ($user_info['is_admin'])
2715
		{
2716
			$context['security_controls_query']['title'] = $txt['query_command_denied'];
2717
			$show_warnings = true;
2718 View Code Duplication
			foreach ($_SESSION['query_command_denied'] as $command => $error)
2719
				$context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>';
2720
		}
2721
		else
2722
		{
2723
			$context['security_controls_query']['title'] = $txt['query_command_denied_guests'];
2724 View Code Duplication
			foreach ($_SESSION['query_command_denied'] as $command => $error)
2725
				$context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>';
2726
		}
2727
	}
2728
2729
	// Are there any members waiting for approval?
2730
	if (allowedTo('moderate_forum') && ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers']))
2731
		$context['warning_controls']['unapproved_members'] = sprintf($txt[$modSettings['unapprovedMembers'] == 1 ? 'approve_one_member_waiting' : 'approve_many_members_waiting'], $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', $modSettings['unapprovedMembers']);
2732
2733
	if (!empty($context['open_mod_reports']) && (empty($user_settings['mod_prefs']) || $user_settings['mod_prefs'][0] == 1))
2734
		$context['warning_controls']['open_mod_reports'] = '<a href="' . $scripturl . '?action=moderate;area=reports">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>';
2735
2736
	if (isset($_SESSION['ban']['cannot_post']))
2737
	{
2738
		// An admin cannot be banned (technically he could), and if it is better he knows.
2739
		$context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], $user_info['is_guest'] ? $txt['guest_title'] : $user_info['name']);
2740
		$show_warnings = true;
2741
2742
		$context['security_controls_ban']['errors']['reason'] = '';
2743
2744
		if (!empty($_SESSION['ban']['cannot_post']['reason']))
2745
			$context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason'];
2746
2747
		if (!empty($_SESSION['ban']['expire_time']))
2748
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>';
2749
		else
2750
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>';
2751
	}
2752
2753
	// Finally, let's show the layer.
2754
	if ($show_warnings || !empty($context['warning_controls']))
2755
		\Template_Layers::getInstance()->addAfter('admin_warning', 'body');
2756
}
2757
2758
/**
2759
 * Load everything necessary for the BBC parsers
2760
 */
2761
function loadBBCParsers()
2762
{
2763
	global $modSettings;
2764
2765
	// Set the default disabled BBC
2766
	if (!empty($modSettings['disabledBBC']))
2767
	{
2768
		if (!is_array($modSettings['disabledBBC']))
2769
			$disabledBBC = explode(',', $modSettings['disabledBBC']);
2770
		else
2771
			$disabledBBC = $modSettings['disabledBBC'];
2772
		\BBC\ParserWrapper::getInstance()->setDisabled(empty($disabledBBC) ? array() : (array) $disabledBBC);
2773
	}
2774
2775
	return 1;
2776
}
2777
2778
/**
2779
 * This is necessary to support data stored in the pre-1.0.8 way (i.e. serialized)
2780
 *
2781
 * @param string $variable The string to convert
2782
 * @param null|callable $save_callback The function that will save the data to the db
2783
 * @return mixed[] the array
2784
 */
2785
function serializeToJson($variable, $save_callback = null)
2786
{
2787
	$array_form = json_decode($variable, true);
2788
2789
	// decoding failed, let's try with unserialize
2790
	if ($array_form === null)
2791
	{
2792
		$array_form = Util::unserialize($variable);
2793
2794
		// If unserialize fails as well, let's just store an empty array
2795
		if ($array_form === false)
2796
		{
2797
			$array_form = array(0, '', 0);
2798
		}
2799
2800
		// Time to update the value if necessary
2801
		if ($save_callback !== null)
2802
		{
2803
			$save_callback($array_form);
2804
		}
2805
	}
2806
2807
	return $array_form;
2808
}
2809