Passed
Pull Request — development (#3540)
by Emanuele
06:47
created

fix_calendar_text()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 63
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 56
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 56
nc 1
nop 0
dl 0
loc 63
ccs 56
cts 56
cp 1
crap 1
rs 8.9599
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
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 *
13
 * @version 2.0 dev
14
 *
15
 */
16
17
use BBC\ParserWrapper;
18
use ElkArte\Cache\Cache;
19
use ElkArte\Debug;
20
use ElkArte\Errors\Errors;
21
use ElkArte\Hooks;
22
use ElkArte\Http\Headers;
23
use ElkArte\Themes\ThemeLoader;
24
use ElkArte\User;
25
use ElkArte\Util;
26
use ElkArte\AttachmentsDirectory;
27
28
/**
29
 * Load the $modSettings array and many necessary forum settings.
30
 *
31
 * What it does:
32
 *
33
 * - load the settings from cache if available, otherwise from the database.
34
 * - sets the timezone
35
 * - checks the load average settings if available.
36
 * - check whether post moderation is enabled.
37
 * - calls add_integration_function
38
 * - calls integrate_pre_include, integrate_pre_load,
39
 *
40
 * @event integrate_load_average is called if load average is enabled
41
 * @event integrate_pre_include to allow including files at startup
42
 * @event integrate_pre_load to call any pre load integration functions.
43
 *
44
 * @global array $modSettings is a giant array of all the forum-wide settings and statistics.
45
 */
46 1
function reloadSettings()
47
{
48 1
	global $modSettings;
49 1
50 1
	$db = database();
51
	$cache = Cache::instance();
52
	$hooks = Hooks::instance();
53 1
54
	// Try to load it from the cache first; it'll never get cached if the setting is off.
55 1
	if (!$cache->getVar($modSettings, 'modSettings', 90))
56
	{
57
		$request = $db->query('', '
58 1
			SELECT 
59
			    variable, value
60 1
			FROM {db_prefix}settings',
61 1
			array()
62
		);
63
		$modSettings = array();
64
		if (!$request)
65 1
		{
66
			Errors::instance()->display_db_error();
67 1
		}
68
		while (($row = $request->fetch_row()))
69 1
		{
70
			$modSettings[$row[0]] = $row[1];
71
		}
72 1
		$request->free_result();
73
74
		// Do a few things to protect against missing settings or settings with invalid values...
75
		if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999)
76
		{
77 1
			$modSettings['defaultMaxTopics'] = 20;
78
		}
79
80
		if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999)
81
		{
82 1
			$modSettings['defaultMaxMessages'] = 15;
83
		}
84
85
		if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999)
86
		{
87 1
			$modSettings['defaultMaxMembers'] = 30;
88
		}
89 1
90
		$modSettings['warning_enable'] = $modSettings['warning_settings'][0];
91
92 1
		$cache->put('modSettings', $modSettings, 90);
93
	}
94 1
95
	$hooks->loadIntegrations();
96
97 1
	// Setting the timezone is a requirement for some functions in PHP >= 5.1.
98
	if (isset($modSettings['default_timezone']))
99
	{
100 1
		date_default_timezone_set($modSettings['default_timezone']);
101
	}
102 1
103
	// Check the load averages?
104
	if (!empty($modSettings['loadavg_enable']))
105
	{
106 1
		if (!$cache->getVar($modSettings['load_average'], 'loadavg', 90))
107
		{
108
			require_once(SUBSDIR . '/Server.subs.php');
109
			$modSettings['load_average'] = detectServerLoad();
110
111
			$cache->put('loadavg', $modSettings['load_average'], 90);
112
		}
113
114
		if ($modSettings['load_average'] !== false)
115
		{
116
			call_integration_hook('integrate_load_average', array($modSettings['load_average']));
117
		}
118
119
		// Let's have at least a zero
120
		if (empty($modSettings['loadavg_forum']) || $modSettings['load_average'] === false)
121
		{
122
			$modSettings['current_load'] = 0;
123
		}
124
		else
125
		{
126
			$modSettings['current_load'] = $modSettings['load_average'];
127
		}
128
129
		if (!empty($modSettings['loadavg_forum']) && $modSettings['current_load'] >= $modSettings['loadavg_forum'])
130
		{
131
			Errors::instance()->display_loadavg_error();
132
		}
133
	}
134
	else
135
	{
136
		$modSettings['current_load'] = 0;
137
	}
138 1
139
	// Is post moderation alive and well?
140
	$modSettings['postmod_active'] = !isset($modSettings['admin_features']) || in_array('pm', explode(',', $modSettings['admin_features']));
141
142 1
	if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off')
143
	{
144 1
		$modSettings['secureCookies'] = 0;
145
	}
146 1
147
	// Integration is cool.
148
	if (defined('ELK_INTEGRATION_SETTINGS'))
149
	{
150 1
		$integration_settings = Util::unserialize(ELK_INTEGRATION_SETTINGS);
0 ignored issues
show
Bug introduced by
The constant ELK_INTEGRATION_SETTINGS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
151
		foreach ($integration_settings as $hook => $function)
152
		{
153
			add_integration_function($hook, $function);
154
		}
155
	}
156
157
	// Any files to pre include?
158
	call_integration_include_hook('integrate_pre_include');
159
160 1
	// Call pre load integration functions.
161
	call_integration_hook('integrate_pre_load');
162
}
163 1
164 1
/**
165
 * Load all the important user information.
166
 *
167
 * What it does:
168
 *
169
 * - sets up the $user_info array
170
 * - assigns $user_info['query_wanna_see_board'] for what boards the user can see.
171
 * - first checks for cookie or integration validation.
172
 * - uses the current session if no integration function or cookie is found.
173
 * - checks password length, if member is activated and the login span isn't over.
174
 * - if validation fails for the user, $id_member is set to 0.
175
 * - updates the last visit time when needed.
176
 *
177
 * @event integrate_verify_user allow for integration to verify a user
178
 * @event integrate_user_info to allow for adding to $user_info array
179
 * @deprecated kept until any trace of $user_info has been completely removed
180
 */
181
function loadUserSettings()
182
{
183
	User::load(true);
184
}
185 1
186 1
/**
187
 * Check for moderators and see if they have access to the board.
188
 *
189
 * What it does:
190
 *
191
 * - sets up the $board_info array for current board information.
192
 * - if cache is enabled, the $board_info array is stored in cache.
193
 * - redirects to appropriate post if only message id is requested.
194
 * - is only used when inside a topic or board.
195
 * - determines the local moderators for the board.
196
 * - adds group id 3 if the user is a local moderator for the board they are in.
197
 * - prevents access if user is not in proper group nor a local moderator of the board.
198
 *
199
 * @event integrate_load_board_query allows to add tables and columns to the query, used
200
 * to add to the $board_info array
201
 * @event integrate_loaded_board called after board_info is populated, allows to add
202
 * directly to $board_info
203
 *
204
 */
205
function loadBoard()
206
{
207
	global $txt, $scripturl, $context, $modSettings, $board_info, $board, $topic;
208
209 13
	$db = database();
210 13
	$cache = Cache::instance();
211
212 13
	// Assume they are not a moderator.
213 13
	User::$info->is_mod = false;
0 ignored issues
show
Bug Best Practice introduced by
The property is_mod does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
214
	// @since 1.0.5 - is_mod takes into account only local (board) moderators,
215
	// and not global moderators, is_moderator is meant to take into account both.
216 13
	User::$info->is_moderator = false;
0 ignored issues
show
Bug Best Practice introduced by
The property is_moderator does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
217
218
	// Start the linktree off empty..
219 13
	$context['linktree'] = array();
220
221
	// Have they by chance specified a message id but nothing else?
222 13
	if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg']))
223
	{
224
		// Make sure the message id is really an int.
225 13
		$_REQUEST['msg'] = (int) $_REQUEST['msg'];
226
227
		// Looking through the message table can be slow, so try using the cache first.
228
		if (!$cache->getVar($topic, 'msg_topic-' . $_REQUEST['msg'], 120))
229
		{
230
			require_once(SUBSDIR . '/Messages.subs.php');
231
			$topic = associatedTopic($_REQUEST['msg']);
232
233
			// So did it find anything?
234
			if ($topic !== false)
235
			{
236
				// Save save save.
237
				$cache->put('msg_topic-' . $_REQUEST['msg'], $topic, 120);
238
			}
239
		}
240
241
		// Remember redirection is the key to avoiding fallout from your bosses.
242
		if (!empty($topic))
243
		{
244
			redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']);
245
		}
246
		else
247
		{
248
			loadPermissions();
249
			new ElkArte\Themes\ThemeLoader();
250
			if (!empty(User::$info->possibly_robot))
251
			{
252
				Headers::instance()
253
					->removeHeader('all')
254
					->headerSpecial('HTTP/1.1 410 Gone')
255
					->sendHeaders();
256
			}
257
258 13
			throw new \ElkArte\Exceptions\Exception('topic_gone', false);
259
		}
260 1
	}
261
262 1
	// Load this board only if it is specified.
263
	if (empty($board) && empty($topic))
264
	{
265 12
		$board_info = array('moderators' => array());
266
267
		return;
268
	}
269
270
	if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2)))
271
	{
272
		// @todo SLOW?
273
		if (!empty($topic))
274
		{
275
			$temp = $cache->get('topic_board-' . $topic, 120);
276
		}
277
		else
278
		{
279
			$temp = $cache->get('board-' . $board, 120);
280
		}
281
282
		if (!empty($temp))
283
		{
284 12
			$board_info = $temp;
285
			$board = (int) $board_info['id'];
286 12
		}
287 12
	}
288
289 12
	if (empty($temp))
290
	{
291 12
		$select_columns = array();
292
		$select_tables = array();
293
		// Wanna grab something more from the boards table or another table at all?
294
		call_integration_hook('integrate_load_board_query', array(&$select_columns, &$select_tables));
295 12
296
		$request = $db->query('', '
297 12
			SELECT
298 12
				c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups,
299 12
				b.id_parent, c.name AS cname, COALESCE(mem.id_member, 0) AS id_moderator,
300 12
				mem.real_name' . (!empty($topic) ? ', b.id_board' : '') . ', b.child_level,
301
				b.id_theme, b.override_theme, b.count_posts, b.id_profile, b.redirect,
302
				b.unapproved_topics, b.unapproved_posts' . (!empty($topic) ? ', t.approved, t.id_member_started' : '') . (!empty($select_columns) ? ', ' . implode(', ', $select_columns) : '') . '
303
			FROM {db_prefix}boards AS b' . (!empty($topic) ? '
304
				INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})' : '') . (!empty($select_tables) ? '
305
				' . implode("\n\t\t\t\t", $select_tables) : '') . '
306 12
				LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat)
307 12
				LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link})
308
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
309
			WHERE b.id_board = {raw:board_link}',
310
			array(
311 12
				'current_topic' => $topic,
312
				'board_link' => empty($topic) ? $db->quote('{int:current_board}', array('current_board' => $board)) : 't.id_board',
313 12
			)
314
		);
315
		// If there aren't any, skip.
316 12
		if ($request->num_rows() > 0)
317
		{
318 10
			$row = $request->fetch_assoc();
319
320
			// Set the current board.
321
			if (!empty($row['id_board']))
322
			{
323 12
				$board = (int) $row['id_board'];
324
			}
325
326 12
			// Basic operating information. (globals... :/)
327 12
			$board_info = array(
328
				'id' => $board,
329 12
				'moderators' => array(),
330 12
				'cat' => array(
331 12
					'id' => $row['id_cat'],
332 12
					'name' => $row['cname']
333 12
				),
334 12
				'name' => $row['bname'],
335 12
				'raw_description' => $row['description'],
336 12
				'description' => $row['description'],
337 12
				'num_topics' => $row['num_topics'],
338 12
				'unapproved_topics' => $row['unapproved_topics'],
339 12
				'unapproved_posts' => $row['unapproved_posts'],
340 12
				'unapproved_user_topics' => 0,
341 12
				'parent_boards' => getBoardParents($row['id_parent']),
342 12
				'parent' => $row['id_parent'],
343 12
				'child_level' => $row['child_level'],
344 12
				'theme' => $row['id_theme'],
345 12
				'override_theme' => !empty($row['override_theme']),
346
				'profile' => $row['id_profile'],
347
				'redirect' => $row['redirect'],
348
				'posts_count' => empty($row['count_posts']),
349 12
				'cur_topic_approved' => empty($topic) || $row['approved'],
350 12
				'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'],
351
			);
352 12
353
			// Load the membergroups allowed, and check permissions.
354
			$board_info['groups'] = $row['member_groups'] == '' ? array() : explode(',', $row['member_groups']);
355
			$board_info['deny_groups'] = $row['deny_member_groups'] == '' ? array() : explode(',', $row['deny_member_groups']);
356 12
357
			call_integration_hook('integrate_loaded_board', array(&$board_info, &$row));
358
359
			do
360
			{
361
				if (!empty($row['id_moderator']))
362
				{
363
					$board_info['moderators'][$row['id_moderator']] = array(
364
						'id' => $row['id_moderator'],
365 12
						'name' => $row['real_name'],
366
						'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]),
367
						'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]) . '">' . $row['real_name'] . '</a>'
368
					);
369 12
				}
370
			} while (($row = $request->fetch_assoc()));
371
372
			// If the board only contains unapproved posts and the user can't approve then they can't see any topics.
373
			// If that is the case do an additional check to see if they have any topics waiting to be approved.
374
			if ($board_info['num_topics'] == 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts'))
375
			{
376
				// Free the previous result
377
				$request->free_result();
378
379
				// @todo why is this using id_topic?
380
				// @todo Can this get cached?
381
				$request = $db->query('', '
382
					SELECT COUNT(id_topic)
383
					FROM {db_prefix}topics
384
					WHERE id_member_started={int:id_member}
385
						AND approved = {int:unapproved}
386
						AND id_board = {int:board}',
387
					array(
388
						'id_member' => User::$info->id,
389
						'unapproved' => 0,
390
						'board' => $board,
391
					)
392 12
				);
393
394
				list ($board_info['unapproved_user_topics']) = $request->fetch_row();
395
			}
396
397
			if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2)))
398
			{
399
				// @todo SLOW?
400 12
				if (!empty($topic))
401
				{
402
					$cache->put('topic_board-' . $topic, $board_info, 120);
403
				}
404
405
				$cache->put('board-' . $board, $board_info, 120);
406
			}
407
		}
408
		else
409
		{
410
			// Otherwise the topic is invalid, there are no moderators, etc.
411
			$board_info = array(
412
				'moderators' => array(),
413 12
				'error' => 'exist'
414
			);
415
			$topic = null;
416 12
			$board = 0;
417
		}
418 10
		$request->free_result();
419
	}
420
421 12
	if (!empty($topic))
422
	{
423
		$_GET['board'] = (int) $board;
424 12
	}
425
426 12
	if (!empty($board))
427
	{
428
		// Now check if the user is a moderator.
429
		User::$info->is_mod = isset($board_info['moderators'][User::$info->id]);
430 12
431
		if (count(array_intersect(User::$info->groups, $board_info['groups'])) == 0 && User::$info->is_admin === false)
0 ignored issues
show
Bug introduced by
It seems like ElkArte\User::info->groups can also be of type null; however, parameter $array of array_intersect() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

431
		if (count(array_intersect(/** @scrutinizer ignore-type */ User::$info->groups, $board_info['groups'])) == 0 && User::$info->is_admin === false)
Loading history...
432
		{
433
			$board_info['error'] = 'access';
434
		}
435
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect(User::$info->groups, $board_info['deny_groups'])) != 0 && User::$info->is_admin === false)
436 12
		{
437 12
			$board_info['error'] = 'access';
438
		}
439 12
440 12
		// Build up the linktree.
441
		$context['linktree'] = array_merge(
442
			$context['linktree'],
443 12
			array(array(
444
					  'url' => getUrl('action', $modSettings['default_forum_action']) . '#c' . $board_info['cat']['id'],
445 12
					  'name' => $board_info['cat']['name']
446 12
				  )
447
			),
448
			array_reverse($board_info['parent_boards']),
449
			array(array(
450
					  'url' => getUrl('board', ['board' => $board, 'start' => '0', 'name' => $board_info['name']]),
451
					  'name' => $board_info['name']
452
				  )
453 12
			)
454 12
		);
455 12
	}
456 12
457
	// Set the template contextual information.
458
	$context['user']['is_mod'] = (bool) User::$info->is_mod;
459 12
	$context['user']['is_moderator'] = (bool) User::$info->is_moderator;
460
	$context['current_topic'] = $topic;
461
	$context['current_board'] = $board;
462
463
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
464
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || User::$info->is_moderator === false))
465
	{
466
		// The permissions and theme need loading, just to make sure everything goes smoothly.
467
		loadPermissions();
468
		new ElkArte\Themes\ThemeLoader();
469
470
		$_GET['board'] = '';
471
		$_GET['topic'] = '';
472
473
		// The linktree should not give the game away mate!
474
		$context['linktree'] = array(
475
			array(
476
				'url' => $scripturl,
477
				'name' => $context['forum_name_html_safe']
478
			)
479
		);
480
481
		// If it's a prefetching agent, stop it
482
		stop_prefetching();
483
484
		// If we're requesting an attachment.
485
		if (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach')
486
		{
487
			ob_end_clean();
488
			Headers::instance()
489
				->removeHeader('all')
490
				->headerSpecial('HTTP/1.1 403 Forbidden')
491
				->sendHeaders();
492
			exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
493
		}
494
		elseif (User::$info->is_guest)
495
		{
496
			ThemeLoader::loadLanguageFile('Errors');
497 12
			is_not_guest($txt['topic_gone']);
498
		}
499
		else
500
		{
501 12
			if (!empty(User::$info->possibly_robot))
502
			{
503
				Headers::instance()
504
					->removeHeader('all')
505
					->headerSpecial('HTTP/1.1 410 Gone')
506
					->sendHeaders();
507
			}
508
509
			throw new \ElkArte\Exceptions\Exception('topic_gone', false);
510
		}
511
	}
512
513
	if (User::$info->is_mod)
514
	{
515
		User::$info->groups = array_merge(User::$info->groups, [3]);
0 ignored issues
show
Bug introduced by
It seems like ElkArte\User::info->groups can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

515
		User::$info->groups = array_merge(/** @scrutinizer ignore-type */ User::$info->groups, [3]);
Loading history...
Bug Best Practice introduced by
The property groups does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
516
	}
517 1
}
518
519 1
/**
520
 * Load this user's permissions.
521 1
 *
522
 * What it does:
523
 *
524
 * - If the user is an admin, validate that they have not been banned.
525
 * - Attempt to load permissions from cache for cache level > 2
526
 * - See if the user is possibly a robot and apply added permissions as needed
527
 * - Load permissions from the general permissions table.
528 1
 * - If inside a board load the necessary board permissions.
529
 * - If the user is not a guest, identify what other boards they have access to.
530 1
 *
531
 * @throws \ElkArte\Exceptions\Exception
532 1
 */
533
function loadPermissions()
534
{
535
	global $board, $board_info, $modSettings;
536
537
	$db = database();
538
539
	if (User::$info->is_admin)
540
	{
541
		banPermissions();
542
543
		return;
544
	}
545
546
	$removals = array();
547
548
	$cache = Cache::instance();
549
550
	if ($cache->isEnabled())
551
	{
552
		$cache_groups = User::$info->groups;
553
		asort($cache_groups);
0 ignored issues
show
Bug introduced by
It seems like $cache_groups can also be of type null; however, parameter $array of asort() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

553
		asort(/** @scrutinizer ignore-type */ $cache_groups);
Loading history...
554
		$cache_groups = implode(',', $cache_groups);
555
556
		// If it's a spider then cache it different.
557
		if (User::$info->possibly_robot)
558
		{
559
			$cache_groups .= '-spider';
560
		}
561
		$cache_key = 'permissions:' . $cache_groups;
562
		$cache_board_key = 'permissions:' . $cache_groups . ':' . $board;
563
564 1
		$temp = array();
565
		if ($cache->levelHigherThan(1) && !empty($board) && $cache->getVar($temp, $cache_board_key, 240) && time() - 240 > $modSettings['settings_updated'])
566 1
		{
567
			list (User::$info->permissions) = $temp;
568
			banPermissions();
569 1
570
			return;
571
		}
572
		elseif ($cache->getVar($temp, $cache_key, 240) && time() - 240 > $modSettings['settings_updated'])
573
		{
574 1
			if (is_array($temp))
575
			{
576 1
				list (User::$info->permissions, $removals) = $temp;
577 1
			}
578
		}
579
	}
580 1
581 1
	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
582
	$spider_restrict = User::$info->possibly_robot && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
583 1
584
	if (empty(User::$info->permissions))
585
	{
586
		// Get the general permissions.
587
		$request = $db->query('', '
588
			SELECT
589 1
				permission, add_deny
590
			FROM {db_prefix}permissions
591
			WHERE id_group IN ({array_int:member_groups})
592 1
				' . $spider_restrict,
593 1
			array(
594
				'member_groups' => User::$info->groups,
595 1
				'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0,
596
			)
597
		);
598
		$permissions = [];
599
		while (($row = $request->fetch_assoc()))
600
		{
601
			if (empty($row['add_deny']))
602 1
			{
603
				$removals[] = $row['permission'];
604
			}
605
			else
606
			{
607
				$permissions[] = $row['permission'];
608
			}
609
		}
610
		User::$info->permissions = $permissions;
0 ignored issues
show
Bug Best Practice introduced by
The property permissions does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
611
		$request->free_result();
612
613
		if (isset($cache_key))
614
		{
615
			$cache->put($cache_key, array((array) User::$info->permissions, !empty($removals) ? $removals : array()), 2);
616
		}
617
	}
618
619
	// Get the board permissions.
620
	if (!empty($board))
621
	{
622
		// Make sure the board (if any) has been loaded by loadBoard().
623
		if (!isset($board_info['profile']))
624
		{
625
			throw new \ElkArte\Exceptions\Exception('no_board');
626
		}
627
628
		$db->fetchQuery('
629
			SELECT
630
				permission, add_deny
631
			FROM {db_prefix}board_permissions
632
			WHERE (id_group IN ({array_int:member_groups})
633
				' . $spider_restrict . ')
634
				AND id_profile = {int:id_profile}',
635
			array(
636
				'member_groups' => User::$info->groups,
637
				'id_profile' => $board_info['profile'],
638
				'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0,
639
			)
640 1
		)->fetch_callback(
641
			function ($row) use (&$removals, &$permissions) {
642
				if (empty($row['add_deny']))
643
				{
644
					$removals[] = $row['permission'];
645 1
				}
646
				else
647
				{
648
					$permissions[] = $row['permission'];
649
				}
650
			}
651 1
		);
652
653
		User::$info->permissions = $permissions;
654 1
	}
655
656
	// Remove all the permissions they shouldn't have ;).
657
	if (!empty($modSettings['permission_enable_deny']))
658
	{
659
		User::$info->permissions = array_diff(User::$info->permissions, $removals);
660
	}
661
662
	if (isset($cache_board_key) && !empty($board) && $cache->levelHigherThan(1))
663
	{
664
		$cache->put($cache_board_key, array(User::$info->permissions, null), 240);
665
	}
666
667 1
	// Banned?  Watch, don't touch..
668
	banPermissions();
669
670
	// Load the mod cache, so we can know what additional boards they should see, but no sense in doing it for guests
671
	if (User::$info->is_guest === false)
672
	{
673
		User::$info->is_moderator = User::$info->is_mod || allowedTo('moderate_board');
0 ignored issues
show
Bug Best Practice introduced by
The property is_moderator does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
674
		if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated'])
675
		{
676 229
			require_once(SUBSDIR . '/Auth.subs.php');
677
			rebuildModCache();
678
		}
679 229
		else
680 229
		{
681 229
			User::$info->mod_cache = $_SESSION['mc'];
0 ignored issues
show
Bug Best Practice introduced by
The property mod_cache does not exist on ElkArte\ValuesContainer. Since you implemented __set, consider adding a @property annotation.
Loading history...
682 229
		}
683
	}
684
}
685
686
/**
687
 * Load a theme, by ID.
688
 *
689
 * What it does:
690
 *
691
 * - identify the theme to be loaded.
692
 * - validate that the theme is valid and that the user has permission to use it
693
 * - load the users theme settings and site settings into $options.
694
 * - prepares the list of folders to search for template loading.
695
 * - identify what smiley set to use.
696
 * - sets up $context['user']
697
 * - detects the users browser and sets a mobile friendly environment if needed
698
 * - loads default JS variables for use in every theme
699
 * - loads default JS scripts for use in every theme
700
 *
701
 * @param int $id_theme = 0
702
 * @param bool $initialize = true
703
 * @deprecated since 2.0; use the theme object
704
 *
705
 */
706
function loadTheme($id_theme = 0, $initialize = true)
707
{
708
	Errors::instance()->log_deprecated('loadTheme()', '\\ElkArte\\Themes\\ThemeLoader');
709
	new ThemeLoader($id_theme, $initialize);
710
}
711
712
/**
713
 * Loads basic user information in to $context['user']
714
 */
715 229
function loadUserContext()
716
{
717
	global $context, $txt, $modSettings;
718 229
719 229
	// Set up the contextual user array.
720 229
	$context['user'] = array(
721 229
		'id' => (int) User::$info->id,
722 229
		'is_logged' => User::$info->is_guest === false,
723 229
		'is_guest' => (bool) User::$info->is_guest,
724 229
		'is_admin' => (bool) User::$info->is_admin,
725
		'is_mod' => (bool) User::$info->is_mod,
726 229
		'is_moderator' => (bool) User::$info->is_moderator,
727 229
		// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator.
728 229
		'can_mod' => (bool) User::$info->canMod($modSettings['postmod_active']),
0 ignored issues
show
Bug introduced by
The method canMod() does not exist on ElkArte\ValuesContainer. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

728
		'can_mod' => (bool) User::$info->/** @scrutinizer ignore-call */ canMod($modSettings['postmod_active']),
Loading history...
729 229
		'username' => User::$info->username,
730 229
		'language' => User::$info->language,
731
		'email' => User::$info->email,
732
		'ignoreusers' => User::$info->ignoreusers,
733
	);
734 229
735
	// @todo Base language is being loaded to late, placed here temporarily
736 82
	ThemeLoader::loadLanguageFile('index+Addons');
737
738 147
	// Something for the guests
739
	if (!$context['user']['is_guest'])
740 146
	{
741
		$context['user']['name'] = User::$info->name;
742
	}
743 229
	elseif (!empty($txt['guest_title']))
744 229
	{
745 229
		$context['user']['name'] = $txt['guest_title'];
746 229
	}
747
748
	$context['user']['smiley_set'] = determineSmileySet(User::$info->smiley_set, $modSettings['smiley_sets_known']);
749
	$context['smiley_enabled'] = User::$info->smiley_set !== 'none';
750
	$context['user']['smiley_path'] = $modSettings['smileys_url'] . '/' . $context['user']['smiley_set'] . '/';
751
}
752
753
/**
754
 * Determine the current user's smiley set
755
 *
756
 * @param array $user_smiley_set
757
 * @param array $known_smiley_sets
758 231
 *
759
 * @return mixed
760 231
 */
761
function determineSmileySet($user_smiley_set, $known_smiley_sets)
762 231
{
763
	global $modSettings, $settings;
764
765
	if ((!in_array($user_smiley_set, explode(',', $known_smiley_sets)) && $user_smiley_set !== 'none') || empty($modSettings['smiley_sets_enable']))
0 ignored issues
show
Bug introduced by
$known_smiley_sets of type array is incompatible with the type string expected by parameter $string of explode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

765
	if ((!in_array($user_smiley_set, explode(',', /** @scrutinizer ignore-type */ $known_smiley_sets)) && $user_smiley_set !== 'none') || empty($modSettings['smiley_sets_enable']))
Loading history...
766
	{
767
		$set = !empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default'];
768
	}
769 231
	else
770
	{
771
		$set = $user_smiley_set;
772
	}
773
774
	return $set;
775
}
776
777
/**
778
 * This loads the bare minimum data.
779
 *
780
 * @deprecated since 2.0; use the theme object
781
 *
782
 * - Needed by scheduled tasks,
783
 * - Needed by any other code that needs language files before the forum (the theme) is loaded.
784
 */
785
function loadEssentialThemeData()
786
{
787
	Errors::instance()->log_deprecated('loadEssentialThemeData()', '\ElkArte\Themes\ThemeLoader::loadEssentialThemeData()');
788
789
	ThemeLoader::loadEssentialThemeData();
790
}
791
792
/**
793
 * Load a template - if the theme doesn't include it, use the default.
794
 *
795
 * What it does:
796
 *
797
 * - loads a template file with the name template_name from the current, default, or base theme.
798
 * - detects a wrong default theme directory and tries to work around it.
799
 * - can be used to only load style sheets by using false as the template name
800
 *   loading of style sheets with this function is deprecated, use loadCSSFile instead
801
 * - if $settings['template_dirs'] is empty, it delays the loading of the template
802
 *
803
 * @param string|false $template_name
804
 * @param string[]|string $style_sheets any style sheets to load with the template
805
 * @param bool $fatal = true if fatal is true, dies with an error message if the template cannot be found
806
 *
807
 * @return bool|null
808
 * @deprecated since 2.0; use the theme object
809
 *
810
 * @uses the requireTemplate() function to actually load the file.
811
 */
812
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
813
{
814
	Errors::instance()->log_deprecated('loadTemplate()', 'theme()->getTemplates()->load()');
815
816
	return theme()->getTemplates()->load($template_name, $style_sheets, $fatal);
817
}
818
819
/**
820
 * Load a sub-template.
821
 *
822
 * What it does:
823
 *
824
 * - loads the sub template specified by sub_template_name, which must be in an already-loaded template.
825
 * - if ?debug is in the query string, shows administrators a marker after every sub template
826
 * for debugging purposes.
827
 *
828
 * @param string $sub_template_name
829
 * @param bool|string $fatal = false
830
 * - $fatal = true is for templates that shouldn't get a 'pretty' error screen
831
 * - $fatal = 'ignore' to skip
832
 *
833
 * @return bool
834
 * @deprecated since 2.0; use the theme object
835
 */
836
function loadSubTemplate($sub_template_name, $fatal = false)
837
{
838
	Errors::instance()->log_deprecated('loadSubTemplate()', 'theme()->getTemplates()->loadSubTemplate()');
839
	theme()->getTemplates()->loadSubTemplate($sub_template_name, $fatal);
840
841
	return true;
842
}
843
844
/**
845
 * Add a CSS file for output later
846
 *
847
 * @param string[]|string $filenames string or array of filenames to work on
848
 * @param array $params = array()
849
 * Keys are the following:
850
 * - ['local'] (true/false): define if the file is local
851
 * - ['fallback'] (true/false): if false  will attempt to load the file
852
 *   from the default theme if not found in the current theme
853
 * - ['stale'] (true/false/string): if true or null, use cache stale,
854
 *   false do not, or used a supplied string
855
 * @param string $id optional id to use in html id=""
856 229
 */
857
function loadCSSFile($filenames, $params = array(), $id = '')
858 229
{
859
	global $context;
860
861
	if (empty($filenames))
862
	{
863 229
		return;
864
	}
865 229
866
	if (!is_array($filenames))
867
	{
868 229
		$filenames = array($filenames);
869
	}
870 12
871
	if (in_array('admin.css', $filenames))
872
	{
873 229
		$filenames[] = $context['theme_variant'] . '/admin' . $context['theme_variant'] . '.css';
874 229
	}
875 229
876 229
	$params['subdir'] = $params['subdir'] ?? 'css';
877
	$params['extension'] = 'css';
878 229
	$params['index_name'] = 'css_files';
879 229
	$params['debug_index'] = 'sheets';
880
881
	loadAssetFile($filenames, $params, $id);
882
}
883
884
/**
885
 * Add a Javascript file for output later
886
 *
887
 * What it does:
888
 *
889
 * - Can be passed an array of filenames, all which will have the same
890
 *   parameters applied,
891
 * - if you need specific parameters on a per file basis, call it multiple times
892
 *
893
 * @param string[]|string $filenames string or array of filenames to work on
894
 * @param array $params = array()
895
 * Keys are the following:
896
 * - ['local'] (true/false): define if the file is local, if file does not
897
 *     start with http its assumed local
898
 * - ['defer'] (true/false): define if the file should load in <head> or before
899
 *     the closing <html> tag
900
 * - ['fallback'] (true/false): if true will attempt to load the file from the
901
 *     default theme if not found in the current this is the default behavior
902
 *     if this is not supplied
903
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
904
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
905
 *     not, or used a supplied string
906
 * @param string $id = '' optional id to use in html id=""
907 231
 */
908
function loadJavascriptFile($filenames, $params = array(), $id = '')
909
{
910
	if (empty($filenames))
911
	{
912 231
		return;
913 231
	}
914 231
915 231
	$params['subdir'] = $params['subdir'] ?? 'scripts';
916
	$params['extension'] = 'js';
917 231
	$params['index_name'] = 'js_files';
918 231
	$params['debug_index'] = 'javascript';
919
920
	loadAssetFile($filenames, $params, $id);
921
}
922
923
/**
924
 * Add an asset (css, js or other) file for output later
925
 *
926
 * What it does:
927
 *
928
 * - Can be passed an array of filenames, all which will have the same
929
 *   parameters applied,
930
 * - If you need specific parameters on a per file basis, call it multiple times
931
 *
932
 * @param string[]|string $filenames string or array of filenames to work on
933
 * @param array $params = array()
934
 * Keys are the following:
935
 * - ['subdir'] (string): the subdirectory of the theme dir the file is in
936
 * - ['extension'] (string): the extension of the file (e.g. css)
937
 * - ['index_name'] (string): the $context index that holds the array of loaded
938
 *     files
939
 * - ['debug_index'] (string): the index that holds the array of loaded
940
 *     files for debugging debug
941
 * - ['local'] (true/false): define if the file is local, if file does not
942
 *     start with http or // (schema-less URLs) its assumed local.
943
 *     The parameter is in fact useful only for files whose name starts with
944
 *     http, in any other case (e.g. passing a local URL) the parameter would
945
 *     fail in properly adding the file to the list.
946
 * - ['defer'] (true/false): define if the file should load in <head> or before
947
 *     the closing <html> tag
948
 * - ['fallback'] (true/false): if true will attempt to load the file from the
949
 *     default theme if not found in the current this is the default behavior
950
 *     if this is not supplied
951
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
952
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
953
 *     not, or used a supplied string
954
 * @param string $id = '' optional id to use in html id=""
955 231
 */
956
function loadAssetFile($filenames, $params = array(), $id = '')
957 231
{
958
	global $settings, $context, $db_show_debug;
959
960
	if (empty($filenames))
961
	{
962 231
		return;
963
	}
964 231
965
	$cache = Cache::instance();
966 161
967
	if (!is_array($filenames))
968
	{
969
		$filenames = array($filenames);
970 231
	}
971
972 231
	// Static values for all these settings
973
	if (!isset($params['stale']) || $params['stale'] === true)
974
	{
975
		$staler_string = CACHE_STALE;
976
	}
977
	elseif (is_string($params['stale']))
978
	{
979
		$staler_string = ($params['stale'][0] === '?' ? $params['stale'] : '?' . $params['stale']);
980
	}
981
	else
982
	{
983 231
		$staler_string = '';
984 231
	}
985
986
	$fallback = !((!empty($params['fallback']) && ($params['fallback'] === false)));
987 231
	$dir = '/' . $params['subdir'] . '/';
988 231
989 231
	// Whoa ... we've done this before yes?
990
	$cache_name = 'load_' . $params['extension'] . '_' . hash('md5', $settings['theme_dir'] . implode('_', $filenames));
991
	$temp = array();
992
	if ($cache->getVar($temp, $cache_name, 600))
993
	{
994
		if (empty($context[$params['index_name']]))
995
		{
996
			$context[$params['index_name']] = array();
997
		}
998
999
		$context[$params['index_name']] += $temp;
1000
1001
		if ($db_show_debug === true)
1002
		{
1003
			foreach ($temp as $temp_params)
1004
			{
1005
				Debug::instance()->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'])) : '') . ')');
1006
			}
1007
		}
1008 231
	}
1009
	else
1010
	{
1011 231
		$this_build = array();
1012
1013
		// All the files in this group use the above parameters
1014 231
		foreach ($filenames as $filename)
1015 231
		{
1016
			// Account for shorthand like admin.ext?xyz11 filenames
1017
			$has_cache_staler = strpos($filename, '.' . $params['extension'] . '?');
1018
			if ($has_cache_staler)
1019
			{
1020
				$cache_staler = $staler_string;
1021
				$params['basename'] = substr($filename, 0, $has_cache_staler + strlen($params['extension']) + 1);
1022 231
			}
1023 231
			else
1024
			{
1025 231
				$cache_staler = $staler_string;
1026
				$params['basename'] = $filename;
1027
			}
1028 231
			$this_id = empty($id) ? strtr(basename($filename), '?', '_') : $id;
1029
1030 231
			// Is this a local file?
1031 231
			if (!empty($params['local']) || (substr($filename, 0, 4) !== 'http' && substr($filename, 0, 2) !== '//'))
1032 231
			{
1033
				$params['local'] = true;
1034
				$params['dir'] = $settings['theme_dir'] . $dir;
1035 231
				$params['url'] = $settings['theme_url'];
1036
1037
				// Fallback if we are not already in the default theme
1038
				if ($fallback && ($settings['theme_dir'] !== $settings['default_theme_dir']) && !file_exists($settings['theme_dir'] . $dir . $params['basename']))
1039
				{
1040
					// Can't find it in this theme, how about the default?
1041
					if (file_exists($settings['default_theme_dir'] . $dir . $params['basename']))
1042
					{
1043
						$filename = $settings['default_theme_url'] . $dir . $params['basename'] . $cache_staler;
1044
						$params['dir'] = $settings['default_theme_dir'] . $dir;
1045
						$params['url'] = $settings['default_theme_url'];
1046
					}
1047
					else
1048
					{
1049
						$filename = false;
1050
					}
1051 231
				}
1052
				else
1053
				{
1054
					$filename = $settings['theme_url'] . $dir . $params['basename'] . $cache_staler;
1055
				}
1056 231
			}
1057
1058 231
			// Add it to the array for use in the template
1059
			if (!empty($filename))
1060 231
			{
1061
				$this_build[$this_id] = $context[$params['index_name']][$this_id] = array('filename' => $filename, 'options' => $params);
1062
1063
				if ($db_show_debug === true)
1064
				{
1065
					Debug::instance()->add($params['debug_index'], $params['basename'] . '(' . (!empty($params['local']) ? (!empty($params['url']) ? basename($params['url']) : basename($params['dir'])) : '') . ')');
1066
				}
1067 231
			}
1068
1069
			// Save it, so we don't have to build this so often
1070 231
			$cache->put($cache_name, $this_build, 600);
1071
		}
1072
	}
1073
}
1074
1075
/**
1076
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
1077
 *
1078
 * @param array $vars array of vars to include in the output done as 'varname' => 'var value'
1079
 * @param bool $escape = false, whether or not to escape the value
1080
 * @deprecated since 2.0; use the theme object
1081
 *
1082
 */
1083
function addJavascriptVar($vars, $escape = false)
1084
{
1085
	Errors::instance()->log_deprecated('addJavascriptVar()', 'theme()->getTemplates()->addJavascriptVar()');
1086
	theme()->addJavascriptVar($vars, $escape);
1087
}
1088
1089
/**
1090
 * Add a block of inline Javascript code to be executed later
1091
 *
1092
 * What it does:
1093
 *
1094
 * - only use this if you have to, generally external JS files are better, but for very small scripts
1095
 *   or for scripts that require help from PHP/whatever, this can be useful.
1096
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
1097
 *
1098
 * @param string $javascript
1099
 * @param bool $defer = false, define if the script should load in <head> or before the closing <html> tag
1100
 * @deprecated since 2.0; use the theme object
1101
 *
1102
 */
1103
function addInlineJavascript($javascript, $defer = false)
1104
{
1105
	Errors::instance()->log_deprecated('addInlineJavascript()', 'theme()->addInlineJavascript()');
1106
	theme()->addInlineJavascript($javascript, $defer);
1107
}
1108
1109
/**
1110
 * Load a language file.
1111
 *
1112
 * - Tries the current and default themes as well as the user and global languages.
1113
 *
1114
 * @param string $template_name
1115
 * @param string $lang = ''
1116
 * @param bool $fatal = true
1117
 * @param bool $force_reload = false
1118
 * @return string The language actually loaded.
1119
 * @deprecated since 2.0; use the theme object
1120
 *
1121
 */
1122
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
1123
{
1124
	Errors::instance()->log_deprecated('loadLanguage()', '\ElkArte\Themes\ThemeLoader::loadLanguageFile()');
1125
1126
	return ThemeLoader::loadLanguageFile($template_name, $lang, $fatal, $force_reload);
1127
}
1128
1129
/**
1130
 * Get all parent boards (requires first parent as parameter)
1131
 *
1132
 * What it does:
1133
 *
1134
 * - It finds all the parents of id_parent, and that board itself.
1135
 * - Additionally, it detects the moderators of said boards.
1136
 * - Returns an array of information about the boards found.
1137 1
 *
1138
 * @param int $id_parent
1139 1
 *
1140 1
 * @return array
1141 1
 * @throws \ElkArte\Exceptions\Exception parent_not_found
1142 1
 */
1143 1
function getBoardParents($id_parent)
1144 1
{
1145 1
	$db = database();
1146 1
	$cache = Cache::instance();
1147
	$boards = array();
1148 1
1149 1
	// First check if we have this cached already.
1150 1
	if (!$cache->getVar($boards, 'board_parents-' . $id_parent, 480))
1151 1
	{
1152 1
		$boards = array();
1153 1
		$original_parent = $id_parent;
1154 1
1155 1
		// Loop while the parent is non-zero.
1156
		while ($id_parent != 0)
1157 1
		{
1158 1
			$result = $db->query('', '
1159 1
				SELECT
1160 1
					b.id_parent, b.name, {int:board_parent} AS id_board, COALESCE(mem.id_member, 0) AS id_moderator,
1161 1
					mem.real_name, b.child_level
1162 1
				FROM {db_prefix}boards AS b
1163 1
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
1164 1
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1165 1
				WHERE b.id_board = {int:board_parent}',
1166 1
				array(
1167 1
					'board_parent' => $id_parent,
1168 1
				)
1169 1
			);
1170
			// In the EXTREMELY unlikely event this happens, give an error message.
1171 1
			if ($result->num_rows() == 0)
1172 1
			{
1173 1
				throw new \ElkArte\Exceptions\Exception('parent_not_found', 'critical');
1174 1
			}
1175 1
			while (($row = $result->fetch_assoc()))
1176 1
			{
1177 1
				if (!isset($boards[$row['id_board']]))
1178 1
				{
1179 1
					$id_parent = $row['id_parent'];
1180 1
					$boards[$row['id_board']] = array(
1181 1
						'url' => getUrl('board', ['board' => $row['id_board'], 'name' => $row['name'], 'start' => '0']),
1182 1
						'name' => $row['name'],
1183 1
						'level' => $row['child_level'],
1184
						'moderators' => array()
1185 1
					);
1186 1
				}
1187 1
1188 1
				// If a moderator exists for this board, add that moderator for all children too.
1189 1
				if (!empty($row['id_moderator']))
1190 1
				{
1191 1
					foreach ($boards as $id => $dummy)
1192 1
					{
1193 1
						$boards[$id]['moderators'][$row['id_moderator']] = array(
1194 1
							'id' => $row['id_moderator'],
1195 1
							'name' => $row['real_name'],
1196 1
							'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]),
1197 1
							'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]) . '">' . $row['real_name'] . '</a>'
1198
						);
1199 1
					}
1200
				}
1201
			}
1202
			$result->free_result();
1203
		}
1204
1205
		$cache->put('board_parents-' . $original_parent, $boards, 480);
1206
	}
1207
1208
	return $boards;
1209
}
1210
1211
/**
1212
 * Attempt to reload our known languages.
1213
 *
1214
 * @param bool $use_cache = true
1215
 *
1216
 * @return array
1217 34
 */
1218
function getLanguages($use_cache = true)
1219 34
{
1220 34
	global $settings;
1221 34
1222
	$cache = Cache::instance();
1223
1224 34
	// Either we don't use the cache, or its expired.
1225
	$languages = array();
1226 34
1227 34
	if (!$use_cache || !$cache->getVar($languages, 'known_languages', $cache->levelLowerThan(2) ? 86400 : 3600))
1228
	{
1229
		// If we don't have our theme information yet, lets get it.
1230 34
		if (empty($settings['default_theme_dir']))
1231
		{
1232 22
			new ThemeLoader(0, false);
1233
		}
1234
1235
		// Default language directories to try.
1236
		$language_directories = array(
1237
			$settings['default_theme_dir'] . '/languages',
1238
			$settings['actual_theme_dir'] . '/languages',
1239
		);
1240
1241 22
		// We possibly have a base theme directory.
1242
		if (!empty($settings['base_theme_dir']))
1243
		{
1244
			$language_directories[] = $settings['base_theme_dir'] . '/languages';
1245 22
		}
1246
1247
		// Remove any duplicates.
1248
		$language_directories = array_unique($language_directories);
1249 22
1250
		foreach ($language_directories as $language_dir)
1251 22
		{
1252
			// Can't look in here... doesn't exist!
1253 22
			if (!file_exists($language_dir))
1254 22
			{
1255 22
				continue;
1256 22
			}
1257 22
1258
			$dir = dir($language_dir);
1259
			while (($entry = $dir->read()))
1260
			{
1261
				// Only directories are interesting
1262
				if ($entry == '..' || !is_dir($dir->path . '/' . $entry))
1263 22
				{
1264
					continue;
1265
				}
1266
1267
				// @todo at some point we may want to simplify that stuff (I mean scanning all the files just for index)
1268
				$file_dir = dir($dir->path . '/' . $entry);
1269
				while (($file_entry = $file_dir->read()))
1270
				{
1271
					// Look for the index language file....
1272
					if (!preg_match('~^index\.(.+)\.php$~', $file_entry, $matches))
1273
					{
1274
						continue;
1275
					}
1276 22
1277
					$languages[$matches[1]] = array(
1278
						'name' => Util::ucwords(strtr($matches[1], array('_' => ' '))),
1279 34
						'selected' => false,
1280
						'filename' => $matches[1],
1281
						'location' => $language_dir . '/' . $entry . '/index.' . $matches[1] . '.php',
1282 34
					);
1283
				}
1284
				$file_dir->close();
1285
			}
1286
			$dir->close();
1287
		}
1288
1289
		// Let's cash in on this deal.
1290
		$cache->put('known_languages', $languages, $cache->isEnabled() && $cache->levelLowerThan(1) ? 86400 : 3600);
1291
	}
1292
1293
	return $languages;
1294 4
}
1295
1296 4
/**
1297
 * Initialize a database connection.
1298
 */
1299 4
function loadDatabase()
1300
{
1301 4
	global $db_prefix, $db_name;
1302
1303
	// Database stuffs
1304 4
	require_once(SOURCEDIR . '/database/Database.subs.php');
1305
1306
	// Safeguard here, if there isn't a valid connection lets put a stop to it.
1307
	try
1308
	{
1309
		$db = database(false);
1310
	}
1311 4
	catch (Exception $e)
1312 4
	{
1313
		Errors::instance()->display_db_error();
1314
	}
1315
1316 4
	// If in SSI mode fix up the prefix.
1317
	if (ELK === 'SSI')
0 ignored issues
show
introduced by
The condition ELK === 'SSI' is always true.
Loading history...
1318
	{
1319
		$db_prefix = $db->fix_prefix($db_prefix, $db_name);
1320
	}
1321
1322 4
	// Case-sensitive database? Let's define a constant.
1323
	// @NOTE: I think it is already taken care by the abstraction, it should be possible to remove
1324 4
	if ($db->case_sensitive() && !defined('DB_CASE_SENSITIVE'))
1325
	{
1326
		define('DB_CASE_SENSITIVE', '1');
1327 4
	}
1328
}
1329
1330
/**
1331
 * Determine the user's avatar type and return the information as an array
1332 4
 *
1333 4
 * @param array $profile array containing the users profile data
1334
 *
1335
 * @return array $avatar
1336 4
 * @todo this function seems more useful than expected, it should be improved. :P
1337
 *
1338 4
 * @event integrate_avatar allows access to $avatar array before it is returned
1339
 */
1340
function determineAvatar($profile)
1341
{
1342 4
	global $modSettings, $settings;
1343 4
1344
	if (empty($profile))
1345
	{
1346 4
		return array();
1347
	}
1348 4
1349
	$avatar_protocol = substr(strtolower($profile['avatar']), 0, 7);
1350
1351 4
	// uploaded avatar?
1352 4
	if ($profile['id_attach'] > 0 && empty($profile['avatar']))
1353
	{
1354 4
		// where are those pesky avatars?
1355 4
		$avatar_url = empty($profile['attachment_type']) ? getUrl('action', ['action' => 'dlattach', 'attach' => $profile['id_attach'], 'type' => 'avatar']) : $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1356
1357
		$avatar = array(
1358 4
			'name' => $profile['avatar'],
1359
			'image' => '<img class="avatar avatarresize" src="' . $avatar_url . '" alt="" />',
1360 4
			'href' => $avatar_url,
1361
			'url' => '',
1362
		);
1363
	}
1364 4
	// remote avatar?
1365
	elseif ($avatar_protocol === 'http://' || $avatar_protocol === 'https:/')
1366
	{
1367 4
		$avatar = array(
1368
			'name' => $profile['avatar'],
1369
			'image' => '<img class="avatar avatarresize" src="' . $profile['avatar'] . '" alt="" />',
1370
			'href' => $profile['avatar'],
1371
			'url' => $profile['avatar'],
1372
		);
1373
	}
1374
	// Gravatar instead?
1375 1
	elseif (!empty($profile['avatar']) && $profile['avatar'] === 'gravatar')
1376
	{
1377
		// Gravatars URL.
1378 1
		$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']) : '');
1379
1380
		$avatar = array(
1381
			'name' => $profile['avatar'],
1382
			'image' => '<img class="avatar avatarresize" src="' . $gravatar_url . '" alt="" />',
1383 1
			'href' => $gravatar_url,
1384
			'url' => $gravatar_url,
1385
		);
1386
	}
1387
	// an avatar from the gallery?
1388
	elseif (!empty($profile['avatar']) && !($avatar_protocol === 'http://' || $avatar_protocol === 'https:/'))
1389
	{
1390
		$avatar = array(
1391 1
			'name' => $profile['avatar'],
1392
			'image' => '<img class="avatar avatarresize" src="' . $modSettings['avatar_url'] . '/' . $profile['avatar'] . '" alt="" />',
1393
			'href' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
1394
			'url' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
1395
		);
1396
	}
1397
	// no custom avatar found yet, maybe a default avatar?
1398 1
	elseif (!empty($modSettings['avatar_default']) && empty($profile['avatar']) && empty($profile['filename']))
1399
	{
1400
		// $settings not initialized? We can't do anything further..
1401
		if (!empty($settings))
1402 1
		{
1403
			// Let's proceed with the default avatar.
1404
			// TODO: This should be incorporated into the theme.
1405
			$avatar = array(
1406
				'name' => '',
1407
				'image' => '<img class="avatar avatarresize" src="' . $settings['images_url'] . '/default_avatar.png" alt="" />',
1408
				'href' => $settings['images_url'] . '/default_avatar.png',
1409
				'url' => 'http://',
1410
			);
1411
		}
1412
		else
1413
		{
1414
			$avatar = array();
1415
		}
1416 13
	}
1417
	// finally ...
1418 13
	else
1419
	{
1420
		$avatar = array(
1421
			'name' => '',
1422
			'image' => '',
1423 13
			'href' => '',
1424
			'url' => ''
1425
		);
1426 13
	}
1427
1428
	// Make sure there's a preview for gravatars available.
1429
	$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']) : '');
1430
1431
	call_integration_hook('integrate_avatar', array(&$avatar, $profile));
1432
1433
	return $avatar;
1434
}
1435
1436
/**
1437
 * Get information about the server
1438
 */
1439 13
function detectServer()
1440
{
1441
	global $context;
1442
	static $server = null;
1443
1444
	if ($server === null)
1445
	{
1446
		$server = new ElkArte\Server($_SERVER);
1447
		$servers = array('iis', 'apache', 'litespeed', 'lighttpd', 'nginx', 'cgi', 'windows');
1448
		$context['server'] = array();
1449 13
		foreach ($servers as $name)
1450
		{
1451
			$context['server']['is_' . $name] = $server->is($name);
1452
		}
1453
1454
		$context['server']['iso_case_folding'] = $server->is('iso_case_folding');
1455
	}
1456
1457
	return $server;
1458
}
1459
1460
/**
1461
 * Returns if a webserver is of type server (apache, nginx, etc)
1462 13
 *
1463
 * @param $server
1464
 *
1465
 * @return bool
1466
 */
1467
function serverIs($server)
1468
{
1469
	return detectServer()->is($server);
1470
}
1471
1472 13
/**
1473
 * Do some important security checks:
1474
 *
1475
 * What it does:
1476
 *
1477
 * - Checks the existence of critical files e.g. install.php
1478
 * - Checks for an active admin session.
1479
 * - Checks cache directory is writable.
1480
 * - Calls secureDirectory to protect attachments & cache.
1481
 * - Checks if the forum is in maintenance mode.
1482
 */
1483
function doSecurityChecks()
1484
{
1485
	global $modSettings, $context, $maintenance, $txt, $options;
1486
1487
	$show_warnings = false;
1488
1489
	$cache = Cache::instance();
1490
1491
	if (allowedTo('admin_forum') && User::$info->is_guest === false)
1492
	{
1493
		// If agreement is enabled, at least the english version shall exists
1494
		if ($modSettings['requireAgreement'] && !file_exists(BOARDDIR . '/agreement.txt'))
1495 13
		{
1496
			$context['security_controls_files']['title'] = $txt['generic_warning'];
1497
			$context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing'];
1498
			$show_warnings = true;
1499
		}
1500
1501
		// Cache directory writable?
1502
		if ($cache->isEnabled() && !is_writable(CACHEDIR))
1503 13
		{
1504
			$context['security_controls_files']['title'] = $txt['generic_warning'];
1505 13
			$context['security_controls_files']['errors']['cache'] = $txt['cache_writable'];
1506
			$show_warnings = true;
1507 13
		}
1508
1509
		if (checkSecurityFiles())
1510
		{
1511
			$show_warnings = true;
1512
		}
1513
1514
		// We are already checking so many files...just few more doesn't make any difference! :P
1515 288
		$attachmentsDir = new AttachmentsDirectory($modSettings, database());
1516 288
		$path = $attachmentsDir->getCurrent();
1517
		secureDirectory($path, true);
1518 288
		secureDirectory(CACHEDIR, false, '"\.(js|css)$"');
1519
1520 1
		// Active admin session?
1521 1
		if (isAdminSessionActive())
1522 1
		{
1523 1
			$context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'adminlogoff', 'redir', '{session_data}'])));
1524
		}
1525 1
1526
		// Maintenance mode enabled?
1527
		if (!empty($maintenance))
1528 1
		{
1529
			$context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'serversettings', '{session_data}'])));
1530 1
		}
1531
1532
		// New updates
1533 288
		if (defined('FORUM_VERSION'))
1534
		{
1535
			$index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION);
1536
			if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index]))
1537
			{
1538
				$show_warnings = true;
1539
				$context['new_version_updates'] = array(
1540
					'title' => $txt['new_version_updates'],
1541
					'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text'])),
1542
				);
1543
			}
1544
		}
1545
	}
1546
1547
	// Check for database errors.
1548
	if (!empty($_SESSION['query_command_denied']))
1549
	{
1550
		if (User::$info->is_admin)
1551
		{
1552
			$context['security_controls_query']['title'] = $txt['query_command_denied'];
1553
			$show_warnings = true;
1554
			foreach ($_SESSION['query_command_denied'] as $command => $error)
1555
			{
1556
				$context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>';
1557
			}
1558
		}
1559
		else
1560
		{
1561
			$context['security_controls_query']['title'] = $txt['query_command_denied_guests'];
1562
			foreach ($_SESSION['query_command_denied'] as $command => $error)
1563
			{
1564
				$context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>';
1565
			}
1566
		}
1567
	}
1568
1569
	// Are there any members waiting for approval?
1570
	if (allowedTo('moderate_forum') && ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers']))
1571
	{
1572
		$context['warning_controls']['unapproved_members'] = sprintf($txt[$modSettings['unapprovedMembers'] == 1 ? 'approve_one_member_waiting' : 'approve_many_members_waiting'], getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => 'approve']), $modSettings['unapprovedMembers']);
1573
	}
1574
1575
	if (!empty($context['open_mod_reports']) && (empty(User::$settings['mod_prefs']) || User::$settings['mod_prefs'][0] == 1))
1576
	{
1577
		$context['warning_controls']['open_mod_reports'] = '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'reports']) . '">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>';
1578
	}
1579
1580
	if (!empty($context['open_pm_reports']) && allowedTo('admin_forum'))
1581
	{
1582
		$context['warning_controls']['open_pm_reports'] = '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'pm_reports']) . '">' . sprintf($txt['pm_reports_waiting'], $context['open_pm_reports']) . '</a>';
1583
	}
1584
1585
	if (isset($_SESSION['ban']['cannot_post']))
1586
	{
1587
		// An admin cannot be banned (technically he could), and if it is better he knows.
1588
		$context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], User::$info->is_guest ? $txt['guest_title'] : User::$info->name);
1589
		$show_warnings = true;
1590
1591
		$context['security_controls_ban']['errors']['reason'] = '';
1592
1593
		if (!empty($_SESSION['ban']['cannot_post']['reason']))
1594
		{
1595
			$context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason'];
1596
		}
1597
1598
		if (!empty($_SESSION['ban']['expire_time']))
1599
		{
1600
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>';
1601
		}
1602
		else
1603
		{
1604
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>';
1605
		}
1606
	}
1607
1608
	// Finally, let's show the layer.
1609
	if ($show_warnings || !empty($context['warning_controls']))
1610
	{
1611
		theme()->getLayers()->addAfter('admin_warning', 'body');
1612
	}
1613
}
1614
1615
/**
1616
 * Load everything necessary for the BBC parsers
1617
 */
1618
function loadBBCParsers()
1619
{
1620
	global $modSettings;
1621
1622
	// Set the default disabled BBC
1623
	if (!empty($modSettings['disabledBBC']))
1624
	{
1625
		if (!is_array($modSettings['disabledBBC']))
1626
		{
1627
			$disabledBBC = explode(',', $modSettings['disabledBBC']);
1628
		}
1629
		else
1630
		{
1631
			$disabledBBC = $modSettings['disabledBBC'];
1632
		}
1633
		ParserWrapper::instance()->setDisabled(empty($disabledBBC) ? array() : $disabledBBC);
1634
	}
1635
1636
	return 1;
1637
}
1638
1639
/**
1640
 * This is necessary to support data stored in the pre-1.0.8 way (i.e. serialized)
1641
 *
1642
 * @param string $variable The string to convert
1643
 * @param null|callable $save_callback The function that will save the data to the db
1644
 * @return array the array
1645
 */
1646
function serializeToJson($variable, $save_callback = null)
1647
{
1648
	$array_form = json_decode($variable, true);
1649
1650
	// decoding failed, let's try with unserialize
1651
	if (!is_array($array_form))
1652
	{
1653
		try
1654
		{
1655
			$array_form = Util::unserialize($variable);
1656
		}
1657
		catch (Exception $e)
1658
		{
1659
			$array_form = false;
1660
		}
1661
1662
		// If unserialize fails as well, let's just store an empty array
1663
		if ($array_form === false)
1664
		{
1665
			$array_form = array(0, '', 0);
1666
		}
1667
1668
		// Time to update the value if necessary
1669
		if ($save_callback !== null)
1670
		{
1671
			$save_callback($array_form);
1672
		}
1673
	}
1674
1675
	return $array_form;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $array_form also could return the type false|string which is incompatible with the documented return type array.
Loading history...
1676
}
1677