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

432
		if (count(array_intersect(/** @scrutinizer ignore-type */ User::$info->groups, $board_info['groups'])) == 0 && User::$info->is_admin === false)
Loading history...
433
		{
434
			$board_info['error'] = 'access';
435
		}
436 12
		if (!empty($modSettings['deny_boards_access']) && count(array_intersect(User::$info->groups, $board_info['deny_groups'])) != 0 && User::$info->is_admin === false)
437 12
		{
438
			$board_info['error'] = 'access';
439 12
		}
440 12
441
		// Build up the linktree.
442
		$context['linktree'] = array_merge(
443 12
			$context['linktree'],
444
			array(array(
445 12
					  'url' => getUrl('action', $modSettings['default_forum_action']) . '#c' . $board_info['cat']['id'],
446 12
					  'name' => $board_info['cat']['name']
447
				  )
448
			),
449
			array_reverse($board_info['parent_boards']),
450
			array(array(
451
					  'url' => getUrl('board', ['board' => $board, 'start' => '0', 'name' => $board_info['name']]),
452
					  'name' => $board_info['name']
453 12
				  )
454 12
			)
455 12
		);
456 12
	}
457
458
	// Set the template contextual information.
459 12
	$context['user']['is_mod'] = (bool) User::$info->is_mod;
460
	$context['user']['is_moderator'] = (bool) User::$info->is_moderator;
461
	$context['current_topic'] = $topic;
462
	$context['current_board'] = $board;
463
464
	// Hacker... you can't see this topic, I'll tell you that. (but moderators can!)
465
	if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] != 'access' || User::$info->is_moderator === false))
466
	{
467
		// The permissions and theme need loading, just to make sure everything goes smoothly.
468
		loadPermissions();
469
		new ElkArte\Themes\ThemeLoader();
470
471
		$_GET['board'] = '';
472
		$_GET['topic'] = '';
473
474
		// The linktree should not give the game away mate!
475
		$context['linktree'] = array(
476
			array(
477
				'url' => $scripturl,
478
				'name' => $context['forum_name_html_safe']
479
			)
480
		);
481
482
		// If it's a prefetching agent, stop it
483
		stop_prefetching();
484
485
		// If we're requesting an attachment.
486
		if (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach')
487
		{
488
			ob_end_clean();
489
			Headers::instance()
490
				->removeHeader('all')
491
				->headerSpecial('HTTP/1.1 403 Forbidden')
492
				->sendHeaders();
493
			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...
494
		}
495
		elseif (User::$info->is_guest)
496
		{
497 12
			Txt::load('Errors');
498
			is_not_guest($txt['topic_gone']);
499
		}
500
		else
501 12
		{
502
			if (!empty(User::$info->possibly_robot))
503
			{
504
				Headers::instance()
505
					->removeHeader('all')
506
					->headerSpecial('HTTP/1.1 410 Gone')
507
					->sendHeaders();
508
			}
509
510
			throw new \ElkArte\Exceptions\Exception('topic_gone', false);
511
		}
512
	}
513
514
	if (User::$info->is_mod)
515
	{
516
		User::$info->groups = array_merge(User::$info->groups, [3]);
0 ignored issues
show
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...
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

516
		User::$info->groups = array_merge(/** @scrutinizer ignore-type */ User::$info->groups, [3]);
Loading history...
517 1
	}
518
}
519 1
520
/**
521 1
 * Load this user's permissions.
522
 *
523
 * What it does:
524
 *
525
 * - If the user is an admin, validate that they have not been banned.
526
 * - Attempt to load permissions from cache for cache level > 2
527
 * - See if the user is possibly a robot and apply added permissions as needed
528 1
 * - Load permissions from the general permissions table.
529
 * - If inside a board load the necessary board permissions.
530 1
 * - If the user is not a guest, identify what other boards they have access to.
531
 *
532 1
 * @throws \ElkArte\Exceptions\Exception
533
 */
534
function loadPermissions()
535
{
536
	global $board, $board_info, $modSettings;
537
538
	$db = database();
539
540
	if (User::$info->is_admin)
541
	{
542
		banPermissions();
543
544
		return;
545
	}
546
547
	$removals = array();
548
549
	$cache = Cache::instance();
550
551
	if ($cache->isEnabled())
552
	{
553
		$cache_groups = User::$info->groups;
554
		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

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

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

766
	if ((!in_array($user_smiley_set, explode(',', /** @scrutinizer ignore-type */ $known_smiley_sets)) && $user_smiley_set !== 'none') || empty($modSettings['smiley_sets_enable']))
Loading history...
767
	{
768
		$set = !empty($settings['smiley_sets_default']) ? $settings['smiley_sets_default'] : $modSettings['smiley_sets_default'];
769 231
	}
770
	else
771
	{
772
		$set = $user_smiley_set;
773
	}
774
775
	return $set;
776
}
777
778
/**
779
 * This loads the bare minimum data.
780
 *
781
 * @deprecated since 2.0; use the theme object
782
 *
783
 * - Needed by scheduled tasks,
784
 * - Needed by any other code that needs language files before the forum (the theme) is loaded.
785
 */
786
function loadEssentialThemeData()
787
{
788
	Errors::instance()->log_deprecated('loadEssentialThemeData()', '\ElkArte\Themes\ThemeLoader::loadEssentialThemeData()');
789
790
	ThemeLoader::loadEssentialThemeData();
791
}
792
793
/**
794
 * Load a template - if the theme doesn't include it, use the default.
795
 *
796
 * What it does:
797
 *
798
 * - loads a template file with the name template_name from the current, default, or base theme.
799
 * - detects a wrong default theme directory and tries to work around it.
800
 * - can be used to only load style sheets by using false as the template name
801
 *   loading of style sheets with this function is deprecated, use loadCSSFile instead
802
 * - if $settings['template_dirs'] is empty, it delays the loading of the template
803
 *
804
 * @param string|false $template_name
805
 * @param string[]|string $style_sheets any style sheets to load with the template
806
 * @param bool $fatal = true if fatal is true, dies with an error message if the template cannot be found
807
 *
808
 * @return bool|null
809
 * @deprecated since 2.0; use the theme object
810
 *
811
 * @uses the requireTemplate() function to actually load the file.
812
 */
813
function loadTemplate($template_name, $style_sheets = array(), $fatal = true)
814
{
815
	Errors::instance()->log_deprecated('loadTemplate()', 'theme()->getTemplates()->load()');
816
817
	return theme()->getTemplates()->load($template_name, $style_sheets, $fatal);
818
}
819
820
/**
821
 * Load a sub-template.
822
 *
823
 * What it does:
824
 *
825
 * - loads the sub template specified by sub_template_name, which must be in an already-loaded template.
826
 * - if ?debug is in the query string, shows administrators a marker after every sub template
827
 * for debugging purposes.
828
 *
829
 * @param string $sub_template_name
830
 * @param bool|string $fatal = false
831
 * - $fatal = true is for templates that shouldn't get a 'pretty' error screen
832
 * - $fatal = 'ignore' to skip
833
 *
834
 * @return bool
835
 * @deprecated since 2.0; use the theme object
836
 */
837
function loadSubTemplate($sub_template_name, $fatal = false)
838
{
839
	Errors::instance()->log_deprecated('loadSubTemplate()', 'theme()->getTemplates()->loadSubTemplate()');
840
	theme()->getTemplates()->loadSubTemplate($sub_template_name, $fatal);
841
842
	return true;
843
}
844
845
/**
846
 * Add a CSS file for output later
847
 *
848
 * @param string[]|string $filenames string or array of filenames to work on
849
 * @param array $params = array()
850
 * Keys are the following:
851
 * - ['local'] (true/false): define if the file is local
852
 * - ['fallback'] (true/false): if false  will attempt to load the file
853
 *   from the default theme if not found in the current theme
854
 * - ['stale'] (true/false/string): if true or null, use cache stale,
855
 *   false do not, or used a supplied string
856 229
 * @param string $id optional id to use in html id=""
857
 */
858 229
function loadCSSFile($filenames, $params = array(), $id = '')
859
{
860
	global $context;
861
862
	if (empty($filenames))
863 229
	{
864
		return;
865 229
	}
866
867
	if (!is_array($filenames))
868 229
	{
869
		$filenames = array($filenames);
870 12
	}
871
872
	if (in_array('admin.css', $filenames))
873 229
	{
874 229
		$filenames[] = $context['theme_variant'] . '/admin' . $context['theme_variant'] . '.css';
875 229
	}
876 229
877
	$params['subdir'] = $params['subdir'] ?? 'css';
878 229
	$params['extension'] = 'css';
879 229
	$params['index_name'] = 'css_files';
880
	$params['debug_index'] = 'sheets';
881
882
	loadAssetFile($filenames, $params, $id);
883
}
884
885
/**
886
 * Add a Javascript file for output later
887
 *
888
 * What it does:
889
 *
890
 * - Can be passed an array of filenames, all which will have the same
891
 *   parameters applied,
892
 * - if you need specific parameters on a per file basis, call it multiple times
893
 *
894
 * @param string[]|string $filenames string or array of filenames to work on
895
 * @param array $params = array()
896
 * Keys are the following:
897
 * - ['local'] (true/false): define if the file is local, if file does not
898
 *     start with http its assumed local
899
 * - ['defer'] (true/false): define if the file should load in <head> or before
900
 *     the closing <html> tag
901
 * - ['fallback'] (true/false): if true will attempt to load the file from the
902
 *     default theme if not found in the current this is the default behavior
903
 *     if this is not supplied
904
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
905
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
906
 *     not, or used a supplied string
907 231
 * @param string $id = '' optional id to use in html id=""
908
 */
909
function loadJavascriptFile($filenames, $params = array(), $id = '')
910
{
911
	if (empty($filenames))
912 231
	{
913 231
		return;
914 231
	}
915 231
916
	$params['subdir'] = $params['subdir'] ?? 'scripts';
917 231
	$params['extension'] = 'js';
918 231
	$params['index_name'] = 'js_files';
919
	$params['debug_index'] = 'javascript';
920
921
	loadAssetFile($filenames, $params, $id);
922
}
923
924
/**
925
 * Add an asset (css, js or other) file for output later
926
 *
927
 * What it does:
928
 *
929
 * - Can be passed an array of filenames, all which will have the same
930
 *   parameters applied,
931
 * - If you need specific parameters on a per file basis, call it multiple times
932
 *
933
 * @param string[]|string $filenames string or array of filenames to work on
934
 * @param array $params = array()
935
 * Keys are the following:
936
 * - ['subdir'] (string): the subdirectory of the theme dir the file is in
937
 * - ['extension'] (string): the extension of the file (e.g. css)
938
 * - ['index_name'] (string): the $context index that holds the array of loaded
939
 *     files
940
 * - ['debug_index'] (string): the index that holds the array of loaded
941
 *     files for debugging debug
942
 * - ['local'] (true/false): define if the file is local, if file does not
943
 *     start with http or // (schema-less URLs) its assumed local.
944
 *     The parameter is in fact useful only for files whose name starts with
945
 *     http, in any other case (e.g. passing a local URL) the parameter would
946
 *     fail in properly adding the file to the list.
947
 * - ['defer'] (true/false): define if the file should load in <head> or before
948
 *     the closing <html> tag
949
 * - ['fallback'] (true/false): if true will attempt to load the file from the
950
 *     default theme if not found in the current this is the default behavior
951
 *     if this is not supplied
952
 * - ['async'] (true/false): if the script should be loaded asynchronously (HTML5)
953
 * - ['stale'] (true/false/string): if true or null, use cache stale, false do
954
 *     not, or used a supplied string
955 231
 * @param string $id = '' optional id to use in html id=""
956
 */
957 231
function loadAssetFile($filenames, $params = array(), $id = '')
958
{
959
	global $settings, $context, $db_show_debug;
960
961
	if (empty($filenames))
962 231
	{
963
		return;
964 231
	}
965
966 161
	$cache = Cache::instance();
967
968
	if (!is_array($filenames))
969
	{
970 231
		$filenames = array($filenames);
971
	}
972 231
973
	// Static values for all these settings
974
	$staler_string = '';
975
	if (!isset($params['stale']) || $params['stale'] === true)
976
	{
977
		$staler_string = CACHE_STALE;
978
	}
979
	elseif (is_string($params['stale']))
980
	{
981
		$staler_string = ($params['stale'][0] === '?' ? $params['stale'] : '?' . $params['stale']);
982
	}
983 231
984 231
	$fallback = !((!empty($params['fallback']) && ($params['fallback'] === false)));
985
	$dir = '/' . $params['subdir'] . '/';
986
987 231
	// Whoa ... we've done this before yes?
988 231
	$cache_name = 'load_' . $params['extension'] . '_' . hash('md5', $settings['theme_dir'] . implode('_', $filenames));
989 231
	$temp = array();
990
	if ($cache->getVar($temp, $cache_name, 600))
991
	{
992
		if (empty($context[$params['index_name']]))
993
		{
994
			$context[$params['index_name']] = array();
995
		}
996
997
		$context[$params['index_name']] += $temp;
998
999
		if ($db_show_debug === true)
1000
		{
1001
			foreach ($temp as $temp_params)
1002
			{
1003
				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'])) : '') . ')');
1004
			}
1005
		}
1006
	}
1007
	else
1008 231
	{
1009
		$this_build = array();
1010
1011 231
		// All the files in this group use the above parameters
1012
		foreach ($filenames as $filename)
1013
		{
1014 231
			// Account for shorthand like admin.ext?xyz11 filenames
1015 231
			$has_cache_staler = strpos($filename, '.' . $params['extension'] . '?');
1016
			$cache_staler = $staler_string;
1017
			if ($has_cache_staler)
1018
			{
1019
				$params['basename'] = substr($filename, 0, $has_cache_staler + strlen($params['extension']) + 1);
1020
			}
1021
			else
1022 231
			{
1023 231
				$params['basename'] = $filename;
1024
			}
1025 231
			$this_id = empty($id) ? strtr(basename($filename), '?', '_') : $id;
1026
1027
			// Is this a local file?
1028 231
			if (!empty($params['local']) || (substr($filename, 0, 4) !== 'http' && substr($filename, 0, 2) !== '//'))
1029
			{
1030 231
				$params['local'] = true;
1031 231
				$params['dir'] = $settings['theme_dir'] . $dir;
1032 231
				$params['url'] = $settings['theme_url'];
1033
1034
				// Fallback if we are not already in the default theme
1035 231
				if ($fallback && ($settings['theme_dir'] !== $settings['default_theme_dir']) && !file_exists($settings['theme_dir'] . $dir . $params['basename']))
1036
				{
1037
					// Can't find it in this theme, how about the default?
1038
					if (file_exists($settings['default_theme_dir'] . $dir . $params['basename']))
1039
					{
1040
						$filename = $settings['default_theme_url'] . $dir . $params['basename'] . $cache_staler;
1041
						$params['dir'] = $settings['default_theme_dir'] . $dir;
1042
						$params['url'] = $settings['default_theme_url'];
1043
					}
1044
					else
1045
					{
1046
						$filename = false;
1047
					}
1048
				}
1049
				else
1050
				{
1051 231
					$filename = $settings['theme_url'] . $dir . $params['basename'] . $cache_staler;
1052
				}
1053
			}
1054
1055
			// Add it to the array for use in the template
1056 231
			if (!empty($filename))
1057
			{
1058 231
				$this_build[$this_id] = array('filename' => $filename, 'options' => $params);
1059
				$context[$params['index_name']][$this_id] = $this_build[$this_id];
1060 231
1061
				if ($db_show_debug === true)
1062
				{
1063
					Debug::instance()->add($params['debug_index'], $params['basename'] . '(' . (!empty($params['local']) ? (!empty($params['url']) ? basename($params['url']) : basename($params['dir'])) : '') . ')');
1064
				}
1065
			}
1066
1067 231
			// Save it, so we don't have to build this so often
1068
			$cache->put($cache_name, $this_build, 600);
1069
		}
1070 231
	}
1071
}
1072
1073
/**
1074
 * Add a Javascript variable for output later (for feeding text strings and similar to JS)
1075
 *
1076
 * @param array $vars array of vars to include in the output done as 'varname' => 'var value'
1077
 * @param bool $escape = false, whether or not to escape the value
1078
 * @deprecated since 2.0; use the theme object
1079
 *
1080
 */
1081
function addJavascriptVar($vars, $escape = false)
1082
{
1083
	Errors::instance()->log_deprecated('addJavascriptVar()', 'theme()->getTemplates()->addJavascriptVar()');
1084
	theme()->addJavascriptVar($vars, $escape);
1085
}
1086
1087
/**
1088
 * Add a block of inline Javascript code to be executed later
1089
 *
1090
 * What it does:
1091
 *
1092
 * - only use this if you have to, generally external JS files are better, but for very small scripts
1093
 *   or for scripts that require help from PHP/whatever, this can be useful.
1094
 * - all code added with this function is added to the same <script> tag so do make sure your JS is clean!
1095
 *
1096
 * @param string $javascript
1097
 * @param bool $defer = false, define if the script should load in <head> or before the closing <html> tag
1098
 * @deprecated since 2.0; use the theme object
1099
 *
1100
 */
1101
function addInlineJavascript($javascript, $defer = false)
1102
{
1103
	Errors::instance()->log_deprecated('addInlineJavascript()', 'theme()->addInlineJavascript()');
1104
	theme()->addInlineJavascript($javascript, $defer);
1105
}
1106
1107
/**
1108
 * Load a language file.
1109
 *
1110
 * - Tries the current and default themes as well as the user and global languages.
1111
 *
1112
 * @param string $template_name
1113
 * @param string $lang = ''
1114
 * @param bool $fatal = true
1115
 * @param bool $force_reload = false
1116
 * @return string The language actually loaded.
1117
 * @deprecated since 2.0; use the theme object
1118
 *
1119
 */
1120
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false)
1121
{
1122
	return Txt::load($template_name, $lang, $fatal);
0 ignored issues
show
Bug introduced by
Are you sure the usage of ElkArte\Languages\Txt::l...te_name, $lang, $fatal) targeting ElkArte\Languages\Txt::load() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
1123
}
1124
1125
/**
1126
 * Get all parent boards (requires first parent as parameter)
1127
 *
1128
 * What it does:
1129
 *
1130
 * - It finds all the parents of id_parent, and that board itself.
1131
 * - Additionally, it detects the moderators of said boards.
1132
 * - Returns an array of information about the boards found.
1133
 *
1134
 * @param int $id_parent
1135
 *
1136
 * @return array
1137 1
 * @throws \ElkArte\Exceptions\Exception parent_not_found
1138
 */
1139 1
function getBoardParents($id_parent)
1140 1
{
1141 1
	$db = database();
1142 1
	$cache = Cache::instance();
1143 1
	$boards = array();
1144 1
1145 1
	// First check if we have this cached already.
1146 1
	if (!$cache->getVar($boards, 'board_parents-' . $id_parent, 480))
1147
	{
1148 1
		$boards = array();
1149 1
		$original_parent = $id_parent;
1150 1
1151 1
		// Loop while the parent is non-zero.
1152 1
		while ($id_parent != 0)
1153 1
		{
1154 1
			$result = $db->query('', '
1155 1
				SELECT
1156
					b.id_parent, b.name, {int:board_parent} AS id_board, COALESCE(mem.id_member, 0) AS id_moderator,
1157 1
					mem.real_name, b.child_level
1158 1
				FROM {db_prefix}boards AS b
1159 1
					LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board)
1160 1
					LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member)
1161 1
				WHERE b.id_board = {int:board_parent}',
1162 1
				array(
1163 1
					'board_parent' => $id_parent,
1164 1
				)
1165 1
			);
1166 1
			// In the EXTREMELY unlikely event this happens, give an error message.
1167 1
			if ($result->num_rows() == 0)
1168 1
			{
1169 1
				throw new \ElkArte\Exceptions\Exception('parent_not_found', 'critical');
1170
			}
1171 1
			while (($row = $result->fetch_assoc()))
1172 1
			{
1173 1
				if (!isset($boards[$row['id_board']]))
1174 1
				{
1175 1
					$id_parent = $row['id_parent'];
1176 1
					$boards[$row['id_board']] = array(
1177 1
						'url' => getUrl('board', ['board' => $row['id_board'], 'name' => $row['name'], 'start' => '0']),
1178 1
						'name' => $row['name'],
1179 1
						'level' => $row['child_level'],
1180 1
						'moderators' => array()
1181 1
					);
1182 1
				}
1183 1
1184
				// If a moderator exists for this board, add that moderator for all children too.
1185 1
				if (!empty($row['id_moderator']))
1186 1
				{
1187 1
					foreach ($boards as $id => $dummy)
1188 1
					{
1189 1
						$boards[$id]['moderators'][$row['id_moderator']] = array(
1190 1
							'id' => $row['id_moderator'],
1191 1
							'name' => $row['real_name'],
1192 1
							'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]),
1193 1
							'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]) . '">' . $row['real_name'] . '</a>'
1194 1
						);
1195 1
					}
1196 1
				}
1197 1
			}
1198
			$result->free_result();
1199 1
		}
1200
1201
		$cache->put('board_parents-' . $original_parent, $boards, 480);
1202
	}
1203
1204
	return $boards;
1205
}
1206
1207
/**
1208
 * Attempt to reload our known languages.
1209
 *
1210
 * @param bool $use_cache = true
1211
 *
1212
 * @return array
1213
 */
1214
function getLanguages($use_cache = true)
1215
{
1216
	global $settings;
1217 34
1218
	$cache = Cache::instance();
1219 34
1220 34
	// Either we don't use the cache, or its expired.
1221 34
	$languages = array();
1222
	$language_dir = SOURCEDIR . '/ElkArte/Languages/Index';
1223
1224 34
	if (!$use_cache || !$cache->getVar($languages, 'known_languages', $cache->levelLowerThan(2) ? 86400 : 3600))
1225
	{
1226 34
		$dir = dir($language_dir . '/');
1227 34
		while (($entry = $dir->read()))
1228
		{
1229
			if ($entry == '.' || $entry == '..')
1230 34
			{
1231
				continue;
1232 22
			}
1233
1234
			$basename = basename($entry, '.php');
1235
			$languages[$basename] = array(
1236
				'name' => $basename,
1237
				'selected' => false,
1238
				'filename' => $entry,
1239
				'location' => $language_dir . '/' . $entry,
1240
			);
1241 22
		}
1242
		$dir->close();
1243
1244
		// Let's cash in on this deal.
1245 22
		$cache->put('known_languages', $languages, $cache->isEnabled() && $cache->levelLowerThan(1) ? 86400 : 3600);
1246
	}
1247
1248
	return $languages;
1249 22
}
1250
1251 22
/**
1252
 * Initialize a database connection.
1253 22
 */
1254 22
function loadDatabase()
1255 22
{
1256 22
	global $db_prefix, $db_name;
1257 22
1258
	// Database stuffs
1259
	require_once(SOURCEDIR . '/database/Database.subs.php');
1260
1261
	// Safeguard here, if there isn't a valid connection lets put a stop to it.
1262
	try
1263 22
	{
1264
		$db = database(false);
1265
	}
1266
	catch (Exception $e)
1267
	{
1268
		Errors::instance()->display_db_error();
1269
	}
1270
1271
	// If in SSI mode fix up the prefix.
1272
	if (ELK === 'SSI')
0 ignored issues
show
introduced by
The condition ELK === 'SSI' is always true.
Loading history...
1273
	{
1274
		$db_prefix = $db->fix_prefix($db_prefix, $db_name);
1275
	}
1276 22
1277
	// Case-sensitive database? Let's define a constant.
1278
	// @NOTE: I think it is already taken care by the abstraction, it should be possible to remove
1279 34
	if ($db->case_sensitive() && !defined('DB_CASE_SENSITIVE'))
1280
	{
1281
		define('DB_CASE_SENSITIVE', '1');
1282 34
	}
1283
}
1284
1285
/**
1286
 * Determine the user's avatar type and return the information as an array
1287
 *
1288
 * @param array $profile array containing the users profile data
1289
 *
1290
 * @return array $avatar
1291
 * @todo this function seems more useful than expected, it should be improved. :P
1292
 *
1293
 * @event integrate_avatar allows access to $avatar array before it is returned
1294 4
 */
1295
function determineAvatar($profile)
1296 4
{
1297
	global $modSettings, $settings;
1298
1299 4
	if (empty($profile))
1300
	{
1301 4
		return array();
1302
	}
1303
1304 4
	$avatar_protocol = substr(strtolower($profile['avatar']), 0, 7);
1305
1306
	// uploaded avatar?
1307
	if ($profile['id_attach'] > 0 && empty($profile['avatar']))
1308
	{
1309
		// where are those pesky avatars?
1310
		$avatar_url = empty($profile['attachment_type']) ? getUrl('action', ['action' => 'dlattach', 'attach' => $profile['id_attach'], 'type' => 'avatar']) : $modSettings['custom_avatar_url'] . '/' . $profile['filename'];
1311 4
1312 4
		$avatar = array(
1313
			'name' => $profile['avatar'],
1314
			'image' => '<img class="avatar avatarresize" src="' . $avatar_url . '" alt="" />',
1315
			'href' => $avatar_url,
1316 4
			'url' => '',
1317
		);
1318
	}
1319
	// remote avatar?
1320
	elseif ($avatar_protocol === 'http://' || $avatar_protocol === 'https:/')
1321
	{
1322 4
		$avatar = array(
1323
			'name' => $profile['avatar'],
1324 4
			'image' => '<img class="avatar avatarresize" src="' . $profile['avatar'] . '" alt="" />',
1325
			'href' => $profile['avatar'],
1326
			'url' => $profile['avatar'],
1327 4
		);
1328
	}
1329
	// Gravatar instead?
1330
	elseif (!empty($profile['avatar']) && $profile['avatar'] === 'gravatar')
1331
	{
1332 4
		// Gravatars URL.
1333 4
		$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']) : '');
1334
1335
		$avatar = array(
1336 4
			'name' => $profile['avatar'],
1337
			'image' => '<img class="avatar avatarresize" src="' . $gravatar_url . '" alt="" />',
1338 4
			'href' => $gravatar_url,
1339
			'url' => $gravatar_url,
1340
		);
1341
	}
1342 4
	// an avatar from the gallery?
1343 4
	elseif (!empty($profile['avatar']) && !($avatar_protocol === 'http://' || $avatar_protocol === 'https:/'))
1344
	{
1345
		$avatar = array(
1346 4
			'name' => $profile['avatar'],
1347
			'image' => '<img class="avatar avatarresize" src="' . $modSettings['avatar_url'] . '/' . $profile['avatar'] . '" alt="" />',
1348 4
			'href' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
1349
			'url' => $modSettings['avatar_url'] . '/' . $profile['avatar'],
1350
		);
1351 4
	}
1352 4
	// no custom avatar found yet, maybe a default avatar?
1353
	elseif (!empty($modSettings['avatar_default']) && empty($profile['avatar']) && empty($profile['filename']))
1354 4
	{
1355 4
		// $settings not initialized? We can't do anything further..
1356
		if (!empty($settings))
1357
		{
1358 4
			// Let's proceed with the default avatar.
1359
			// TODO: This should be incorporated into the theme.
1360 4
			$avatar = array(
1361
				'name' => '',
1362
				'image' => '<img class="avatar avatarresize" src="' . $settings['images_url'] . '/default_avatar.png" alt="" />',
1363
				'href' => $settings['images_url'] . '/default_avatar.png',
1364 4
				'url' => 'http://',
1365
			);
1366
		}
1367 4
		else
1368
		{
1369
			$avatar = array();
1370
		}
1371
	}
1372
	// finally ...
1373
	else
1374
	{
1375 1
		$avatar = array(
1376
			'name' => '',
1377
			'image' => '',
1378 1
			'href' => '',
1379
			'url' => ''
1380
		);
1381
	}
1382
1383 1
	// Make sure there's a preview for gravatars available.
1384
	$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']) : '');
1385
1386
	call_integration_hook('integrate_avatar', array(&$avatar, $profile));
1387
1388
	return $avatar;
1389
}
1390
1391 1
/**
1392
 * Get information about the server
1393
 */
1394
function detectServer()
1395
{
1396
	global $context;
1397
	static $server = null;
1398 1
1399
	if ($server === null)
1400
	{
1401
		$server = new ElkArte\Server($_SERVER);
1402 1
		$servers = array('iis', 'apache', 'litespeed', 'lighttpd', 'nginx', 'cgi', 'windows');
1403
		$context['server'] = array();
1404
		foreach ($servers as $name)
1405
		{
1406
			$context['server']['is_' . $name] = $server->is($name);
1407
		}
1408
1409
		$context['server']['iso_case_folding'] = $server->is('iso_case_folding');
1410
	}
1411
1412
	return $server;
1413
}
1414
1415
/**
1416 13
 * Returns if a webserver is of type server (apache, nginx, etc)
1417
 *
1418 13
 * @param $server
1419
 *
1420
 * @return bool
1421
 */
1422
function serverIs($server)
1423 13
{
1424
	return detectServer()->is($server);
1425
}
1426 13
1427
/**
1428
 * Do some important security checks:
1429
 *
1430
 * What it does:
1431
 *
1432
 * - Checks the existence of critical files e.g. install.php
1433
 * - Checks for an active admin session.
1434
 * - Checks cache directory is writable.
1435
 * - Calls secureDirectory to protect attachments & cache.
1436
 * - Checks if the forum is in maintenance mode.
1437
 */
1438
function doSecurityChecks()
1439 13
{
1440
	global $modSettings, $context, $maintenance, $txt, $options;
1441
1442
	$show_warnings = false;
1443
1444
	$cache = Cache::instance();
1445
1446
	if (allowedTo('admin_forum') && User::$info->is_guest === false)
1447
	{
1448
		// If agreement is enabled, at least the english version shall exists
1449 13
		if ($modSettings['requireAgreement'] && !file_exists(BOARDDIR . '/agreement.txt'))
1450
		{
1451
			$context['security_controls_files']['title'] = $txt['generic_warning'];
1452
			$context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing'];
1453
			$show_warnings = true;
1454
		}
1455
1456
		// Cache directory writable?
1457
		if ($cache->isEnabled() && !is_writable(CACHEDIR))
1458
		{
1459
			$context['security_controls_files']['title'] = $txt['generic_warning'];
1460
			$context['security_controls_files']['errors']['cache'] = $txt['cache_writable'];
1461
			$show_warnings = true;
1462 13
		}
1463
1464
		if (checkSecurityFiles())
1465
		{
1466
			$show_warnings = true;
1467
		}
1468
1469
		// We are already checking so many files...just few more doesn't make any difference! :P
1470
		$attachmentsDir = new AttachmentsDirectory($modSettings, database());
1471
		$path = $attachmentsDir->getCurrent();
1472 13
		secureDirectory($path, true);
1473
		secureDirectory(CACHEDIR, false, '"\.(js|css)$"');
1474
1475
		// Active admin session?
1476
		if (isAdminSessionActive())
1477
		{
1478
			$context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'adminlogoff', 'redir', '{session_data}'])));
1479
		}
1480
1481
		// Maintenance mode enabled?
1482
		if (!empty($maintenance))
1483
		{
1484
			$context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'serversettings', '{session_data}'])));
1485
		}
1486
1487
		// New updates
1488
		if (defined('FORUM_VERSION'))
1489
		{
1490
			$index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION);
1491
			if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index]))
1492
			{
1493
				$show_warnings = true;
1494
				$context['new_version_updates'] = array(
1495 13
					'title' => $txt['new_version_updates'],
1496
					'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text'])),
1497
				);
1498
			}
1499
		}
1500
	}
1501
1502
	// Check for database errors.
1503 13
	if (!empty($_SESSION['query_command_denied']))
1504
	{
1505 13
		if (User::$info->is_admin)
1506
		{
1507 13
			$context['security_controls_query']['title'] = $txt['query_command_denied'];
1508
			$show_warnings = true;
1509
			foreach ($_SESSION['query_command_denied'] as $command => $error)
1510
			{
1511
				$context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>';
1512
			}
1513
		}
1514
		else
1515 288
		{
1516 288
			$context['security_controls_query']['title'] = $txt['query_command_denied_guests'];
1517
			foreach ($_SESSION['query_command_denied'] as $command => $error)
1518 288
			{
1519
				$context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>';
1520 1
			}
1521 1
		}
1522 1
	}
1523 1
1524
	// Are there any members waiting for approval?
1525 1
	if (allowedTo('moderate_forum') && ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers']))
1526
	{
1527
		$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']);
1528 1
	}
1529
1530 1
	if (!empty($context['open_mod_reports']) && (empty(User::$settings['mod_prefs']) || User::$settings['mod_prefs'][0] == 1))
1531
	{
1532
		$context['warning_controls']['open_mod_reports'] = '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'reports']) . '">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>';
1533 288
	}
1534
1535
	if (!empty($context['open_pm_reports']) && allowedTo('admin_forum'))
1536
	{
1537
		$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>';
1538
	}
1539
1540
	if (isset($_SESSION['ban']['cannot_post']))
1541
	{
1542
		// An admin cannot be banned (technically he could), and if it is better he knows.
1543
		$context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], User::$info->is_guest ? $txt['guest_title'] : User::$info->name);
1544
		$show_warnings = true;
1545
1546
		$context['security_controls_ban']['errors']['reason'] = '';
1547
1548
		if (!empty($_SESSION['ban']['cannot_post']['reason']))
1549
		{
1550
			$context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason'];
1551
		}
1552
1553
		if (!empty($_SESSION['ban']['expire_time']))
1554
		{
1555
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>';
1556
		}
1557
		else
1558
		{
1559
			$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>';
1560
		}
1561
	}
1562
1563
	// Finally, let's show the layer.
1564
	if ($show_warnings || !empty($context['warning_controls']))
1565
	{
1566
		theme()->getLayers()->addAfter('admin_warning', 'body');
1567
	}
1568
}
1569
1570
/**
1571
 * Load everything necessary for the BBC parsers
1572
 */
1573
function loadBBCParsers()
1574
{
1575
	global $modSettings;
1576
1577
	// Set the default disabled BBC
1578
	if (!empty($modSettings['disabledBBC']))
1579
	{
1580
		if (!is_array($modSettings['disabledBBC']))
1581
		{
1582
			$disabledBBC = explode(',', $modSettings['disabledBBC']);
1583
		}
1584
		else
1585
		{
1586
			$disabledBBC = $modSettings['disabledBBC'];
1587
		}
1588
		ParserWrapper::instance()->setDisabled(empty($disabledBBC) ? array() : $disabledBBC);
1589
	}
1590
1591
	return 1;
1592
}
1593
1594
/**
1595
 * This is necessary to support data stored in the pre-1.0.8 way (i.e. serialized)
1596
 *
1597
 * @param string $variable The string to convert
1598
 * @param null|callable $save_callback The function that will save the data to the db
1599
 * @return array the array
1600
 */
1601
function serializeToJson($variable, $save_callback = null)
1602
{
1603
	$array_form = json_decode($variable, true);
1604
1605
	// decoding failed, let's try with unserialize
1606
	if (!is_array($array_form))
1607
	{
1608
		try
1609
		{
1610
			$array_form = Util::unserialize($variable);
1611
		}
1612
		catch (Exception $e)
1613
		{
1614
			$array_form = false;
1615
		}
1616
1617
		// If unserialize fails as well, let's just store an empty array
1618
		if ($array_form === false)
1619
		{
1620
			$array_form = array(0, '', 0);
1621
		}
1622
1623
		// Time to update the value if necessary
1624
		if ($save_callback !== null)
1625
		{
1626
			$save_callback($array_form);
1627
		}
1628
	}
1629
1630
	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...
1631
}
1632