sp_embed_image()   F
last analyzed

Complexity

Conditions 14
Paths 1536

Size

Total Lines 66
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 14
eloc 37
nc 1536
nop 6
dl 0
loc 66
rs 2.1
c 1
b 1
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package SimplePortal ElkArte
5
 *
6
 * @author SimplePortal Team
7
 * @copyright 2015-2021 SimplePortal Team
8
 * @license BSD 3-clause
9
 * @version 1.0.0
10
 */
11
12
use BBC\ParserWrapper;
13
14
/**
15
 * Return if the portal is active, or active in a given area.
16
 *
17
 * @return bool
18
 */
19
function sp_is_active()
20
{
21
	global $modSettings, $context, $settings, $maintenance, $user_info;
22
23
	$context['disable_sp'] = false;
24
25
	// This to ensure BrowserDetector has run, Elkarte 1.1.4 will return this value properly, until then
26
	// we need to make the call and then check browser_body_id below.
27
	isBrowser('mobile');
28
29
	// Frontpage hooks are called early in the flow
30
	if (!isset($user_info['is_guest']))
31
	{
32
		loadUserSettings();
33
	}
34
35
	// Need to determine if we are even active
36
	if ((!empty($modSettings['sp_disableMobile']) && $context['browser_body_id'] === 'mobile') && empty($_GET['page']) && empty($_GET['article'])
37
		|| !empty($settings['disable_sp'])
38
		|| !empty($modSettings['disable_sp'])
39
		|| empty($modSettings['sp_portal_mode'])
40
		|| ((!empty($modSettings['sp_maintenance']) || !empty($maintenance)) && !allowedTo('admin_forum'))
41
		|| isset($_GET['debug'])
42
		|| (empty($modSettings['allow_guestAccess']) && $user_info['is_guest'])
43
		|| (empty($modSettings['front_page']) || $modSettings['front_page'] !== 'PortalMain_Controller'))
44
	{
45
		$context['disable_sp'] = true;
46
	}
47
48
	return !$context['disable_sp'];
49
}
50
51
/**
52
 * Initializes the portal, outputs all the blocks as needed
53
 *
54
 * @param boolean $standalone
55
 * @throws \Elk_Exception
56
 */
57
function sportal_init($standalone = false)
58
{
59
	global $context, $scripturl, $modSettings, $settings;
60
61
	define('SPORTAL_VERSION', '1.0.0');
62
	define('SPORTAL_STALE', 'sp100');
63
64
	if ((isset($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach'))
65
	{
66
		return;
67
	}
68
69
	if ((isset($_REQUEST['xml']) || isset($_REQUEST['api'])) && ((isset($_REQUEST['action']) && $_REQUEST['action'] !== 'shoutbox')))
70
	{
71
		return;
72
	}
73
74
	// Not running standalone then we need to load in some template information
75
	if (!$standalone)
76
	{
77
		// Load the portal css and the default css if its not yet loaded.
78
		loadCSSFile('portal.css', ['stale' => SPORTAL_STALE]);
79
80
		// rtl css as well?
81
		if (!empty($context['right_to_left']))
82
		{
83
			loadCSSFile('portal_rtl.css');
84
		}
85
86
		if (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], array('admin')))
87
		{
88
			loadLanguage('SPortalAdmin');
89
		}
90
91
		if (!isset($settings['sp_images_url']))
92
		{
93
			if (file_exists($settings['theme_dir'] . '/images/sp'))
94
			{
95
				$settings['sp_images_url'] = $settings['theme_url'] . '/images/sp';
96
			}
97
			else
98
			{
99
				$settings['sp_images_url'] = $settings['default_theme_url'] . '/images/sp';
100
			}
101
		}
102
	}
103
104
	// Portal not enabled, or mobile, or debug, or maintenance, or .... then bow out now
105
	if (!sp_is_active())
106
	{
107
		if ($standalone)
108
		{
109
			$get_string = '';
110
			foreach ($_GET as $get_var => $get_value)
111
			{
112
				$get_string .= $get_var . (!empty($get_value) ? '=' . $get_value : '') . ';';
113
			}
114
115
			redirectexit(substr($get_string, 0, -1));
116
		}
117
118
		return;
119
	}
120
121
	// Not standalone means we need to load some portal specific information in to context
122
	if (!$standalone)
123
	{
124
		require_once(SUBSDIR . '/spblocks/SPAbstractBlock.class.php');
125
126
		// Not running via ssi then we need to get SSI for its functions
127
		if (ELK !== 'SSI')
0 ignored issues
show
introduced by
The condition ELK !== 'SSI' is always false.
Loading history...
128
		{
129
			require_once(BOARDDIR . '/SSI.php');
130
		}
131
132
		// Portal specific templates and language
133
		Templates::instance()->load('Portal');
134
		loadLanguage('SPortal');
135
136
		if (!empty($modSettings['sp_maintenance']) && !allowedTo('sp_admin'))
137
		{
138
			$modSettings['sp_portal_mode'] = 0;
139
		}
140
141
		if (empty($modSettings['sp_standalone_url']))
142
		{
143
			$modSettings['sp_standalone_url'] = '';
144
		}
145
146
		if ($modSettings['sp_portal_mode'] == 3)
147
		{
148
			$context += array(
149
				'portal_url' => $modSettings['sp_standalone_url'],
150
				'page_title' => $context['forum_name'],
151
			);
152
		}
153
		else
154
		{
155
			$context += array(
156
				'portal_url' => $scripturl,
157
			);
158
		}
159
160
		if ($modSettings['sp_portal_mode'] == 1)
161
		{
162
			$context['linktree'][0] = array(
163
				'url' => $scripturl . '?action=forum',
164
				'name' => $context['forum_name'],
165
			);
166
		}
167
168
		if (!empty($context['linktree']) && $modSettings['sp_portal_mode'] == 1)
169
		{
170
			foreach ($context['linktree'] as $key => $tree)
171
			{
172
				if (strpos($tree['url'], '#c') !== false && strpos($tree['url'], 'action=forum#c') === false)
173
				{
174
					$context['linktree'][$key]['url'] = str_replace('#c', '?action=forum#c', $tree['url']);
175
				}
176
			}
177
		}
178
	}
179
	else
180
	{
181
		$_GET['action'] = 'portal';
182
	}
183
184
	// Load the headers if necessary.
185
	sportal_init_headers();
186
187
	// Load permissions
188
	sportal_load_permissions();
189
190
	// Play with our blocks
191
	$context['standalone'] = $standalone;
192
	sportal_load_blocks();
193
194
	// Determine if we will show blocks on the portal page
195
	$context['SPortal']['on_portal'] = sportal_process_visibility('portal');
196
197
	// Add the portal template
198
	if (!Template_Layers::instance()->hasLayers(true) && !in_array('portal', Template_Layers::instance()->getLayers()))
199
	{
200
		Template_Layers::instance()->add('portal');
201
	}
202
}
203
204
/**
205
 * Deals with the initialization of SimplePortal headers.
206
 */
207
function sportal_init_headers()
208
{
209
	global $modSettings, $txt, $user_info, $scripturl;
210
	static $initialized;
211
212
	// If already loaded just return
213
	if (!empty($initialized))
214
	{
215
		return $initialized;
216
	}
217
218
	// Generate a safe scripturl
219
	$safe_scripturl = $scripturl;
220
	$current_request = empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'];
221
222
	if (strpos($scripturl, 'www.') !== false && strpos($current_request, 'www.') === false)
223
	{
224
		$safe_scripturl = str_replace('://www.', '://', $scripturl);
225
	}
226
	elseif (strpos($scripturl, 'www.') === false && strpos($current_request, 'www.') !== false)
227
	{
228
		$safe_scripturl = str_replace('://', '://www.', $scripturl);
229
	}
230
231
	// The shoutbox may fail to function in certain cases without using a safe scripturl
232
	addJavascriptVar(array('sp_script_url' => '\'' . $safe_scripturl . '\''));
233
234
	// Load up some javascript!
235
	loadJavascriptFile('portal.js', ['stale' => SPORTAL_STALE]);
236
237
	// We use drag and sort blocks for the front page
238
	$javascript = '';
239
	if ($modSettings['sp_portal_mode'] == 1)
240
	{
241
		// Javascript to allow D&D ordering of the front page blocks, not for guests
242
		if (empty($_REQUEST['action']) && empty($_REQUEST['board']) && !($user_info['is_guest'] || $user_info['id'] == 0))
243
		{
244
			$modSettings['jquery_include_ui'] = true;
245
			$javascript .= '
246
				// Set up our sortable call
247
				$().elkSortable({
248
					sa: "userblockorder",
249
					error: "' . $txt['portal_order_error'] . '",
250
					title: "' . $txt['portal_order_title'] . '",
251
					handle: ".sp_drag_header",
252
					tag: ".sp_column",
253
					opacity: 0.9,
254
					connect: ".sp_column",
255
					containment: "#main_content_section",
256
					tolerance: "pointer",
257
					href: "/",
258
					placeholder: "ui-state-highlight",
259
					axis: "",
260
				});';
261
		}
262
	}
263
264
	if ($modSettings['sp_resize_images'])
265
	{
266
		$javascript .= '
267
		createEventListener(window);
268
		window.addEventListener("load", sp_image_resize, false);';
269
	}
270
271
	// Let the template know we have some inline JS to display
272
	if (!empty($javascript))
273
	{
274
		addInlineJavascript($javascript, true);
275
	}
276
277
	// Mark it as done so we don't do it again
278
	return true;
279
}
280
281
/**
282
 * Loads the permissions for the profiles
283
 */
284
function sportal_load_permissions()
285
{
286
	global $context, $user_info;
287
288
	$profiles = sportal_get_profiles(null, 1);
289
	$allowed = array();
290
291
	foreach ($profiles as $profile)
292
	{
293
		$result = false;
294
295
		if (!empty($profile['groups_denied']) && count(array_intersect($user_info['groups'], $profile['groups_denied'])) > 0)
296
		{
297
			$result = false;
298
		}
299
		elseif (!empty($profile['groups_allowed']) && count(array_intersect($user_info['groups'], $profile['groups_allowed'])) > 0)
300
		{
301
			$result = true;
302
		}
303
304
		if ($result)
305
		{
306
			$allowed[] = $profile['id'];
307
		}
308
	}
309
310
	$context['SPortal']['permissions'] = array(
311
		'profiles' => $allowed,
312
		'query' => empty($allowed) ? '0=1' : 'FIND_IN_SET(%s, \'' . implode(',', $allowed) . '\')',
313
	);
314
}
315
316
/**
317
 * Loads all the defined portal blocks in to context
318
 */
319
function sportal_load_blocks()
320
{
321
	global $context, $modSettings, $options;
322
323
	$context['SPortal']['sides'] = array(
324
		5 => array(
325
			'id' => '5',
326
			'name' => 'header',
327
			'active' => true,
328
		),
329
		1 => array(
330
			'id' => '1',
331
			'name' => 'left',
332
			'active' => !empty($modSettings['showleft']),
333
		),
334
		2 => array(
335
			'id' => '2',
336
			'name' => 'top',
337
			'active' => true,
338
		),
339
		3 => array(
340
			'id' => '3',
341
			'name' => 'bottom',
342
			'active' => true,
343
		),
344
		4 => array(
345
			'id' => '4',
346
			'name' => 'right',
347
			'active' => !empty($modSettings['showright']),
348
		),
349
		6 => array(
350
			'id' => '6',
351
			'name' => 'footer',
352
			'active' => true,
353
		),
354
	);
355
356
	// Get the blocks in the system
357
	$blocks = getBlockInfo(null, null, true, true, true);
358
359
	// If the member has arranged the blocks, display them like that
360
	if (!empty($options['sp_block_layout']))
361
	{
362
		$layout = @unserialize($options['sp_block_layout']);
363
364
		// If some bad arrangement data found its way in
365
		if ($layout === false)
366
		{
367
			resetMemberLayout();
368
		}
369
		else
370
		{
371
			foreach ($layout as $id => $column)
372
			{
373
				if (empty($column) || empty($id) || !$context['SPortal']['sides'][$id]['active'])
374
				{
375
					continue;
376
				}
377
378
				// For each custom arranged block
379
				foreach ($column as $item)
380
				{
381
					if (empty($blocks[$item]))
382
					{
383
						continue;
384
					}
385
386
					// Style information for the block, based on its style profile
387
					$blocks[$item]['style'] = sportal_select_style($blocks[$item]['styles']);
388
389
					// For each moved block, instantiate it and run setup
390
					$blocks[$item]['instance'] = sp_instantiate_block($blocks[$item]['type'], $blocks[$item]['id']);
391
					$blocks[$item]['instance']->setup($blocks[$item]['parameters'], $blocks[$item]['id']);
392
					$context['SPortal']['blocks'][$id][] = $blocks[$item];
393
394
					// Don't do this again
395
					unset($blocks[$item]);
396
				}
397
398
				$context['SPortal']['blocks']['custom_arrange'] = true;
399
			}
400
		}
401
	}
402
403
	if (!isset($context['SPortal']['blocks']))
404
	{
405
		$context['SPortal']['blocks'] = array();
406
	}
407
408
	// For each active block, determine the style and get an instance of it for use
409
	foreach ($blocks as $block)
410
	{
411
		if (!$context['SPortal']['sides'][$block['column']]['active'] || empty($block['type']))
412
		{
413
			continue;
414
		}
415
416
		$block['style'] = sportal_select_style($block['styles']);
417
418
		$block['instance'] = sp_instantiate_block($block['type'], $block['id']);
419
		$block['instance']->setup($block['parameters'], $block['id']);
420
421
		$context['SPortal']['sides'][$block['column']]['last'] = $block['id'];
422
		$context['SPortal']['blocks'][$block['column']][] = $block;
423
	}
424
425
	foreach ($context['SPortal']['sides'] as $side)
426
	{
427
		if (empty($context['SPortal']['blocks'][$side['id']]))
428
		{
429
			$context['SPortal']['sides'][$side['id']]['active'] = false;
430
		}
431
432
		$context['SPortal']['sides'][$side['id']]['collapsed'] = $context['user']['is_guest']
433
			? !empty($_COOKIE['sp_' . $side['name']])
434
			: !empty($options['sp_' . $side['name']]);
435
	}
436
}
437
438
/**
439
 * A shortcut that takes care of instantiating the block and returning the instance
440
 *
441
 * @param string $name The type of the block (without "_Block" at the end)
442
 * @param int $id The id of the block, to allow multiple same types on the page
443
 * @return object
444
 */
445
function sp_instantiate_block($name, $id = 0)
446
{
447
	static $instances = array(), $db = null;
448
449
	if ($db === null)
450
	{
451
		$db = database();
452
	}
453
454
	if (!isset($instances[$name . '_' . $id]))
455
	{
456
		require_once(SUBSDIR . '/spblocks/' . str_replace('_', '', $name) . '.block.php');
457
458
		$class = $name . '_Block';
459
		$instances[$name. '_' . $id] = new $class($db);
460
	}
461
462
	return $instances[$name. '_' . $id];
463
}
464
465
/**
466
 * If a member has arranged blocks in some bizarre fashion, this will reset the layout to
467
 * the default one
468
 */
469
function resetMemberLayout()
470
{
471
	global $settings, $user_info;
472
473
	$db = database();
474
475
	$db->query('', '
476
		DELETE FROM {db_prefix}themes
477
		WHERE id_theme = {int:current_theme}
478
			AND variable = {string:theme_variable}
479
			AND id_member = {int:id_member}',
480
		array(
481
			'current_theme' => $settings['theme_id'],
482
			'theme_variable' => 'sp_block_layout',
483
			'id_member' => $user_info['id'],
484
		)
485
	);
486
487
	// Clear the user theme cache to reflect the changes now
488
	cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60);
0 ignored issues
show
Unused Code introduced by
The call to cache_put_data() has too many arguments starting with null. ( Ignorable by Annotation )

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

488
	/** @scrutinizer ignore-call */ 
489
 cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
489
}
490
491
/**
492
 * This function, returns all of the information about particular blocks.
493
 *
494
 * @param int|null $column_id
495
 * @param int|null $block_id
496
 * @param bool|null $state
497
 * @param bool|null $show
498
 * @param bool|null $permission
499
 *
500
 * @return array of block values
501
 */
502
function getBlockInfo($column_id = null, $block_id = null, $state = null, $show = null, $permission = null)
503
{
504
	global $context, $options, $txt;
505
506
	$db = database();
507
508
	$query = array();
509
	$parameters = array();
510
511
	if (!empty($column_id))
512
	{
513
		$query[] = 'spb.col = {int:col}';
514
		$parameters['col'] = $column_id;
515
	}
516
517
	if (!empty($block_id))
518
	{
519
		$query[] = 'spb.id_block = {int:id_block}';
520
		$parameters['id_block'] = $block_id;
521
	}
522
523
	if (!empty($permission))
524
	{
525
		$query[] = sprintf($context['SPortal']['permissions']['query'], 'spb.permissions');
526
	}
527
528
	if (!empty($state))
529
	{
530
		$query[] = 'spb.state = {int:state}';
531
		$parameters['state'] = 1;
532
	}
533
534
	$request = $db->query('', '
535
		SELECT
536
			spb.id_block, spb.label, spb.type, spb.col, spb.row, spb.permissions, spb.state,
537
			spb.force_view, spb.visibility, spb.styles, spp.variable, spp.value
538
		FROM {db_prefix}sp_blocks AS spb
539
			LEFT JOIN {db_prefix}sp_parameters AS spp ON (spp.id_block = spb.id_block)' . (!empty($query) ? '
540
		WHERE ' . implode(' AND ', $query) : '') . '
541
		ORDER BY spb.col, spb.row', $parameters
542
	);
543
	$return = array();
544
	$show_it = array();
545
	while ($row = $db->fetch_assoc($request))
546
	{
547
		if (!empty($show))
548
		{
549
			if (isset($show_it[$row['visibility']]) && $show_it[$row['visibility']] === false)
550
			{
551
				continue;
552
			}
553
			elseif (!isset($show_it[$row['visibility']]))
554
			{
555
				$show_it[$row['visibility']] = sportal_check_visibility($row['visibility']);
556
557
				if ($show_it[$row['visibility']] === false)
558
				{
559
					continue;
560
				}
561
			}
562
		}
563
564
		if (!isset($return[$row['id_block']]))
565
		{
566
			// Allow custom blocks to load txt values.
567
			if (empty($txt['sp_function_' . $row['type'] . '_label']))
568
				sp_instantiate_block($row['type']);
569
570
			$return[$row['id_block']] = array(
571
				'id' => $row['id_block'],
572
				'label' => $row['label'],
573
				'type' => $row['type'],
574
				'type_text' => !empty($txt['sp_function_' . $row['type'] . '_label']) ? $txt['sp_function_' . $row['type'] . '_label'] : $txt['sp_function_unknown_label'],
575
				'column' => $row['col'],
576
				'row' => $row['row'],
577
				'permissions' => $row['permissions'],
578
				'styles' => $row['styles'],
579
				'visibility' => $row['visibility'],
580
				'state' => empty($row['state']) ? 0 : 1,
581
				'force_view' => $row['force_view'],
582
				'collapsed' => $context['user']['is_guest'] ? !empty($_COOKIE['sp_block_' . $row['id_block']]) : !empty($options['sp_block_' . $row['id_block']]),
583
				'parameters' => array(),
584
			);
585
		}
586
587
		if (!empty($row['variable']))
588
		{
589
			$return[$row['id_block']]['parameters'][$row['variable']] = $row['value'];
590
		}
591
	}
592
	$db->free_result($request);
593
594
	return $return;
595
}
596
597
/**
598
 * Function to get a block's display/show information.
599
 *
600
 * @param string[]|string|null $query
601
 *
602
 * @return boolean|array
603
 */
604
function sportal_process_visibility($query)
605
{
606
	global $context, $modSettings;
607
	static $page_info, $category_info, $article_info;
608
609
	// Fetch any page, category or article as needed
610
	if (!empty($_GET['page']) && empty($page_info) && (empty($context['current_action']) || $context['current_action'] === 'portal'))
611
	{
612
		$page_info = sportal_get_pages($_GET['page'], true, true);
613
	}
614
615
	if (!empty($_GET['category']) && empty($category_info) && (empty($context['current_action']) || $context['current_action'] === 'portal'))
616
	{
617
		$category_info = sportal_get_categories($_GET['category'], true, true);
618
	}
619
620
	if (!empty($_GET['article']) && empty($article_info) && (empty($context['current_action']) || $context['current_action'] === 'portal'))
621
	{
622
		require_once(SUBSDIR . '/PortalArticle.subs.php');
623
		$article_info = sportal_get_articles($_GET['article'], true, true);
624
	}
625
626
	// Some variables for easy checking.
627
	$action = !empty($context['current_action']) ? $context['current_action'] : '';
628
	$sub_action = !empty($context['current_subaction']) ? $context['current_subaction'] : '';
629
	$board = !empty($context['current_board']) ? 'b' . $context['current_board'] : '';
630
	$topic = !empty($context['current_topic']) ? 't' . $context['current_topic'] : '';
631
	$page = !empty($page_info['id']) ? 'p' . $page_info['id'] : '';
632
	$category = !empty($category_info['id']) ? 'c' . $category_info['id'] : '';
633
	$article = !empty($article_info['id']) ? 'a' . $article_info['id'] : '';
634
	$portal = (empty($action) && empty($sub_action) && empty($board) && empty($topic) && empty($page) && empty($category) && empty($article) && ELK !== 'SSI' && $modSettings['sp_portal_mode'] == 1) || $action === 'portal' || !empty($context['standalone']);
0 ignored issues
show
introduced by
The condition ELK !== 'SSI' is always false.
Loading history...
635
	$forum = (empty($action) && empty($sub_action) && empty($board) && empty($topic) && empty($page) && empty($category) && empty($article) && ELK !== 'SSI' && $modSettings['sp_portal_mode'] != 1) || $action === 'forum';
0 ignored issues
show
introduced by
The condition ELK !== 'SSI' is always false.
Loading history...
636
637
	// Will hopefully get larger in the future.
638
	$portal_actions = array(
639
		'articles' => true,
640
		'start' => true,
641
		'theme' => true,
642
		'PHPSESSID' => true,
643
		'wwwRedirect' => true,
644
		'www' => true,
645
		'variant' => true,
646
		'language' => true,
647
		'action' => array('portal'),
648
	);
649
650
	// Set some action exceptions so they use a common root name
651
	$exceptions = array(
652
		'post' => array('announce', 'editpoll', 'emailuser', 'post2', 'sendtopic'),
653
		'register' => array('activate', 'coppa'),
654
		'forum' => array('collapse'),
655
		'admin' => array('credits', 'theme', 'viewquery', 'viewsmfile'),
656
		'moderate' => array('groups'),
657
		'login' => array('reminder'),
658
		'profile' => array('trackip', 'viewprofile'),
659
	);
660
661
	// Still, we might not be in portal!
662
	if (!empty($_GET) && empty($context['standalone']))
663
	{
664
		foreach ($_GET as $key => $value)
665
		{
666
			if (preg_match('~^news\d+$~', $key))
667
			{
668
				continue;
669
			}
670
671
			if (!isset($portal_actions[$key]))
672
			{
673
				$portal = false;
674
			}
675
			elseif (is_array($portal_actions[$key]) && !in_array($value, $portal_actions[$key]))
0 ignored issues
show
Bug introduced by
It seems like $portal_actions[$key] can also be of type true; however, parameter $haystack of in_array() 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

675
			elseif (is_array($portal_actions[$key]) && !in_array($value, /** @scrutinizer ignore-type */ $portal_actions[$key]))
Loading history...
676
			{
677
				$portal = false;
678
			}
679
		}
680
	}
681
682
	// Set the action to a common one, eg reminder => login
683
	foreach ($exceptions as $key => $exception)
684
	{
685
		if (in_array($action, $exception))
686
		{
687
			$action = $key;
688
		}
689
	}
690
691
	// Complex display options first...
692
	if (($boundary = strpos($query, '$php')) !== false)
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type null and string[]; however, parameter $haystack of strpos() does only seem to accept string, 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

692
	if (($boundary = strpos(/** @scrutinizer ignore-type */ $query, '$php')) !== false)
Loading history...
693
	{
694
		$code = substr($query, $boundary + 4);
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type null and string[]; however, parameter $string of substr() does only seem to accept string, 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

694
		$code = substr(/** @scrutinizer ignore-type */ $query, $boundary + 4);
Loading history...
695
696
		$variables = array(
697
			'{$action}' => "'$action'",
698
			'{$sa}' => "'$sub_action'",
699
			'{$board}' => "'$board'",
700
			'{$topic}' => "'$topic'",
701
			'{$page}' => "'$page'",
702
			'{$category}' => "'$category'",
703
			'{$article}' => "'$article'",
704
			'{$portal}' => $portal,
705
			'{$forum}' => $forum,
706
		);
707
708
		try
709
		{
710
			return eval(str_replace(array_keys($variables), array_values($variables), un_htmlspecialchars($code)) . ';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
711
		}
712
		catch (\Throwable $e)
713
		{
714
			return un_htmlspecialchars($code);
0 ignored issues
show
Bug Best Practice introduced by
The expression return un_htmlspecialchars($code) returns the type string which is incompatible with the documented return type array|boolean.
Loading history...
715
		}
716
	}
717
718
	if (!empty($query))
719
	{
720
		$query = explode(',', $query);
0 ignored issues
show
Bug introduced by
It seems like $query can also be of type string[]; however, parameter $string of explode() does only seem to accept string, 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

720
		$query = explode(',', /** @scrutinizer ignore-type */ $query);
Loading history...
721
	}
722
	else
723
	{
724
		return false;
725
	}
726
727
	// Take care of custom actions.
728
	$special = array();
729
	$exclude = array();
730
	foreach ($query as $value)
731
	{
732
		if (!isset($value[0]))
733
		{
734
			continue;
735
		}
736
737
		$item = '';
738
739
		// Is this a weird action?
740
		if ($value[0] === '~')
741
		{
742
			if (strpos($value, '|') !== false)
743
			{
744
				list ($name, $item) = explode('|', substr($value, 1));
745
			}
746
			else
747
			{
748
				$name = substr($value, 1);
749
			}
750
751
			if (empty($item))
752
			{
753
				$special[$name] = true;
754
			}
755
			else
756
			{
757
				$special[$name][] = $item;
758
			}
759
		}
760
		// Might be excluding something!
761
		elseif ($value[0] === '-')
762
		{
763
			// We still may have weird things...
764
			if ($value[1] === '~')
765
			{
766
				if (strpos($value, '|') !== false)
767
				{
768
					list ($name, $item) = explode('|', substr($value, 2));
769
				}
770
				else
771
				{
772
					$name = substr($value, 2);
773
				}
774
775
				if (empty($item))
776
				{
777
					$exclude['special'][$name] = true;
778
				}
779
				else
780
				{
781
					$exclude['special'][$name][] = $item;
782
				}
783
			}
784
			else
785
			{
786
				$exclude['regular'][] = substr($value, 1);
787
			}
788
		}
789
	}
790
791
	// We don't want to show it on this action/page/board?
792
	if (!empty($exclude['regular']) && count(array_intersect(array($action, $page, $board, $category, $article), $exclude['regular'])) > 0)
793
	{
794
		return false;
795
	}
796
797
	// Maybe we don't want to show it in somewhere special.
798
	if (!empty($exclude['special']))
799
	{
800
		foreach ($exclude['special'] as $key => $value)
801
		{
802
			if (isset($_GET[$key]))
803
			{
804
				if (is_array($value) && !in_array($_GET[$key], $value))
805
				{
806
					continue;
807
				}
808
				else
809
				{
810
					return false;
811
				}
812
			}
813
		}
814
	}
815
816
	// Did we disable all blocks for this action?
817
	if (!empty($modSettings['sp_' . $action . 'IntegrationHide']))
818
	{
819
		return false;
820
	}
821
	// If we will display show the block.
822
	elseif (in_array('all', $query))
823
	{
824
		return true;
825
	}
826
	// If we are on portal, show portal blocks; if we are on forum, show forum blocks.
827
	elseif (($portal && in_array('portal', $query)) || ($forum && in_array('forum', $query)))
828
	{
829
		return true;
830
	}
831
	elseif (!empty($action) && $action !== 'portal' && (in_array('allaction', $query) || in_array($action, $query)))
832
	{
833
		return true;
834
	}
835
	elseif (!empty($board) && (in_array('allboard', $query) || in_array($board, $query)))
836
	{
837
		return true;
838
	}
839
	elseif (!empty($page) && (in_array('allpage', $query) || in_array($page, $query)))
840
	{
841
		return true;
842
	}
843
	elseif (!empty($category) && (in_array('allcategory', $query) || in_array($category, $query)))
844
	{
845
		return true;
846
	}
847
	elseif (!empty($article) && (in_array('allarticle', $query) || in_array($article, $query)))
848
	{
849
		return true;
850
	}
851
852
	// For addons using weird urls...
853
	foreach ($special as $key => $value)
854
	{
855
		if (isset($_GET[$key]))
856
		{
857
			if (is_array($value) && !in_array($_GET[$key], $value))
858
			{
859
				continue;
860
			}
861
			else
862
			{
863
				return true;
864
			}
865
		}
866
	}
867
868
	// Ummm, no block!
869
	return false;
870
}
871
872
/**
873
 * Determines if the block should be shown for this area, action, etc
874
 *
875
 * @param int $visibility_id
876
 *
877
 * @return mixed if to show the block or not
878
 */
879
function sportal_check_visibility($visibility_id)
880
{
881
	global $context;
882
883
	static $visibilities;
884
885
	// Load the visibility profiles so we can put them to use on the blocks
886
	if (!isset($visibilities))
887
	{
888
		$visibilities = sportal_get_profiles(null, 3);
889
	}
890
891
	// See if we can show this block, here, now, for this ...
892
	if ($visibility_id == '0' && !isset($visibilities[$visibility_id]))
893
	{
894
		// No id, assume its off
895
		return false;
896
	}
897
	elseif (isset($visibilities[$visibility_id]))
898
	{
899
		// Can we show it here, should we?
900
		if ($context['browser_body_id'] === 'mobile' && empty($visibilities[$visibility_id]['mobile_view']))
901
		{
902
			return false;
903
		}
904
		else
905
		{
906
			return sportal_process_visibility($visibilities[$visibility_id]['final']);
907
		}
908
	}
909
	else
910
	{
911
		// No block for you
912
		return false;
913
	}
914
}
915
916
/**
917
 * This is a simple function that loads calendar data for the portal as infrequently as possible
918
 *
919
 * @param string $type type of data to load, events, birthdays, etc
920
 * @param string $low_date don't load data before this date
921
 * @param string|boolean $high_date don't load data after this date, false for no limit
922
 *
923
 * @return array
924
 */
925
function sp_loadCalendarData($type, $low_date, $high_date = false)
926
{
927
	static $loaded;
928
929
	if (!isset($loaded))
930
	{
931
		require_once(SUBSDIR . '/Calendar.subs.php');
932
933
		$loaded = array(
934
			'getEvents' => 'getEventRange',
935
			'getBirthdays' => 'getBirthdayRange',
936
			'getHolidays' => 'getHolidayRange',
937
		);
938
	}
939
940
	if (!empty($loaded[$type]))
941
	{
942
		return $loaded[$type]($low_date, ($high_date === false ? $low_date : $high_date));
943
	}
944
	else
945
	{
946
		return array();
947
	}
948
}
949
950
/**
951
 * This is a small script to load colors for SPortal.
952
 *
953
 * @param int[] $users
954
 *
955
 * @return bool|array
956
 */
957
function sp_loadColors($users = array())
958
{
959
	global $color_profile, $scripturl, $modSettings;
960
961
	$db = database();
962
963
	// This is for later, if you like to disable colors ;)
964
	if (!empty($modSettings['sp_disableColor']))
965
	{
966
		return false;
967
	}
968
969
	// Can't just look for no users. :P
970
	if (empty($users))
971
	{
972
		return false;
973
	}
974
975
	// Make sure it's an array.
976
	$users = !is_array($users) ? array($users) : array_unique($users);
0 ignored issues
show
introduced by
The condition is_array($users) is always true.
Loading history...
977
978
	// Check up the array :)
979
	foreach ($users as $k => $u)
980
	{
981
		$u = (int) $u;
982
983
		if (empty($u))
984
		{
985
			unset($users[$k]);
986
		}
987
		else
988
		{
989
			$users[$k] = $u;
990
		}
991
	}
992
993
	$loaded_ids = array();
994
995
	// Is this a totally new variable?
996
	if (empty($color_profile))
997
	{
998
		$color_profile = array();
999
	}
1000
	// Otherwise, we will need to do some reformatting of the old data.
1001
	else
1002
	{
1003
		foreach ($users as $k => $u)
1004
		{
1005
			if (isset($color_profile[$u]))
1006
			{
1007
				$loaded_ids[] = $u;
1008
				unset($users[$k]);
1009
			}
1010
		}
1011
	}
1012
1013
	// Make sure that we have some users.
1014
	if (empty($users))
1015
	{
1016
		return empty($loaded_ids) ? false : $loaded_ids;
1017
	}
1018
1019
	// Correct array pointer for the user
1020
	reset($users);
1021
1022
	// Load the data.
1023
	$request = $db->query('', '
1024
		SELECT
1025
			mem.id_member, mem.member_name, mem.real_name, mem.id_group,
1026
			mg.online_color AS member_group_color,
1027
			pg.online_color AS post_group_color
1028
		FROM {db_prefix}members AS mem
1029
			LEFT JOIN {db_prefix}membergroups AS pg ON (pg.id_group = mem.id_post_group)
1030
			LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = mem.id_group)
1031
		WHERE mem.id_member ' . ((count($users) == 1) ? '= {int:current}' : 'IN ({array_int:users})'),
1032
		array(
1033
			'users' => $users,
1034
			'current' => (int) current($users),
1035
		)
1036
	);
1037
	// Go through each of the users.
1038
	while ($row = $db->fetch_assoc($request))
1039
	{
1040
		$loaded_ids[] = $row['id_member'];
1041
		$color_profile[$row['id_member']] = $row;
1042
		$onlineColor = !empty($row['member_group_color']) ? $row['member_group_color'] : $row['post_group_color'];
1043
		$color_profile[$row['id_member']]['color'] = $onlineColor;
1044
		$color_profile[$row['id_member']]['link'] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '"' . (!empty($onlineColor) ? ' style="color: ' . $onlineColor . ';"' : '') . '>' . $row['real_name'] . '</a>';
1045
		$color_profile[$row['id_member']]['colored_name'] = (!empty($onlineColor) ? '<span style="color: ' . $onlineColor . ';">' : '') . $row['real_name'] . (!empty($onlineColor) ? '</span>' : '');
1046
	}
1047
	$db->free_result($request);
1048
1049
	// Return the necessary data.
1050
	return empty($loaded_ids) ? false : $loaded_ids;
1051
}
1052
1053
/**
1054
 * Builds an image tag for use in templates
1055
 *
1056
 * @param string $name
1057
 * @param string $alt
1058
 * @param int|null $width
1059
 * @param int|null $height
1060
 * @param string|boolean $title
1061
 * @param int|null $id
1062
 *
1063
 * @return string
1064
 */
1065
function sp_embed_image($name, $alt = '', $width = null, $height = null, $title = true, $id = null)
1066
{
1067
	global $modSettings, $settings, $txt;
1068
	static $default_alt, $randomizer;
1069
1070
	loadLanguage('SPortal');
1071
1072
	// Some default alt text settings for our standard images
1073
	if (!isset($default_alt))
1074
	{
1075
		$default_alt = array(
1076
			'dot' => $txt['sp-dot'],
1077
			'stars' => $txt['sp-star'],
1078
			'arrow' => $txt['sp-arrow'],
1079
			'modify' => $txt['modify'],
1080
			'edit' => $txt['edit'],
1081
			'delete' => $txt['delete'],
1082
			'trash' => $txt['delete'],
1083
			'delete_small' => $txt['delete'],
1084
			'history' => $txt['sp_shoutbox_history'],
1085
			'refresh' => $txt['sp_shoutbox_refresh'],
1086
			'smiley' => $txt['sp_shoutbox_smiley'],
1087
			'style' => $txt['sp_shoutbox_style'],
1088
			'bin' => $txt['sp_shoutbox_prune'],
1089
			'move' => $txt['sp_move'],
1090
			'add' => $txt['sp_add'],
1091
			'items' => $txt['sp_items'],
1092
			'given' => $txt['sp_likes_given'],
1093
			'received' => $txt['sp_likes_received'],
1094
		);
1095
	}
1096
1097
	if (!isset($randomizer) || $randomizer > 7)
1098
	{
1099
		$randomizer = 0;
1100
	}
1101
1102
	$randomizer++;
1103
1104
	// Use a default alt text if available and none was supplied
1105
	if (empty($alt))
1106
	{
1107
		$alt = $default_alt[$name] ?? '';
1108
	}
1109
1110
	// You want a title, use the alt text if we can
1111
	if ($title === true)
1112
	{
1113
		$title = !empty($alt) ? $alt : '';
1114
	}
1115
1116
	if (empty($alt))
1117
	{
1118
		$alt = $name;
1119
	}
1120
1121
	// dots using random colors
1122
	if (in_array($name, array('dot', 'star')) && empty($modSettings['sp_disable_random_bullets']))
1123
	{
1124
		$name .= $randomizer;
1125
	}
1126
1127
	// Build the image tag
1128
	return '<img src="' . $settings['sp_images_url'] . '/' . $name . '.png" alt="' . $alt . '"' . (!empty($title)
1129
			? ' title="' . $title . '"' : '') . (!empty($width) ? ' width="' . $width . '"' : '') . (!empty($height)
1130
			? ' height="' . $height . '"' : '') . (!empty($id) ? ' id="' . $id . '"' : '') . ' />';
1131
}
1132
1133
/**
1134
 * Builds a sprite class tag for use in templates
1135
 *
1136
 * @param string $name
1137
 * @param string $title
1138
 * @param string $extraclass
1139
 * @param string $spriteclass
1140
 *
1141
 * @return string
1142
 */
1143
function sp_embed_class($name, $title = '', $extraclass = '', $spriteclass = 'dot')
1144
{
1145
	global $modSettings, $txt;
1146
	static $default_title, $randomizer;
1147
1148
	loadLanguage('SPortal');
1149
1150
	// Some default title text settings for our standard sprites
1151
	if (!isset($default_title))
1152
	{
1153
		$default_title = array(
1154
			'dot' => '',
1155
			'stars' => $txt['sp-star'],
1156
			'arrow' => $txt['sp-arrow'],
1157
			'modify' => $txt['modify'],
1158
			'delete' => $txt['delete'],
1159
			'trash' => $txt['delete'],
1160
			'edit' => $txt['edit'],
1161
			'delete_small' => $txt['delete'],
1162
			'history' => $txt['sp_shoutbox_history'],
1163
			'refresh' => $txt['sp_shoutbox_refresh'],
1164
			'smiley' => $txt['sp_shoutbox_smiley'],
1165
			'style' => $txt['sp_shoutbox_style'],
1166
			'bin' => $txt['sp_shoutbox_prune'],
1167
			'move' => $txt['sp_move'],
1168
			'add' => $txt['sp_add'],
1169
			'items' => $txt['sp_items'],
1170
			'given' => $txt['sp_likes_given'],
1171
			'received' => $txt['sp_likes_received'],
1172
		);
1173
	}
1174
1175
	// Use a default title text if available and none was supplied
1176
	if (empty($title))
1177
	{
1178
		$title = $default_title[$name] ?? $name;
1179
	}
1180
1181
	// dots / start using colors
1182
	if (in_array($name, array('dot', 'star')) && empty($modSettings['sp_disable_random_bullets']))
1183
	{
1184
		// Loop through the dot colors
1185
		if (!isset($randomizer) || $randomizer > 7)
1186
		{
1187
			$randomizer = 0;
1188
		}
1189
1190
		$randomizer++;
1191
		$name = $name . $randomizer;
1192
	}
1193
1194
	// Build the attributes
1195
	return 'class="' . $spriteclass . ' ' . $name . (!empty($extraclass) ? ' ' . $extraclass : '') . '" title="' . $title . '"';
1196
}
1197
1198
/**
1199
 * Implodes or Expands a set of style attributes
1200
 *
1201
 * @param string $action implode or explode
1202
 * @param string $setting string of style options joined on name~value|name~value
1203
 * @param boolean $process
1204
 *
1205
 * @return array()
1206
 */
1207
function sportal_parse_style($action, $setting = '', $process = false)
1208
{
1209
	static $process_cache;
1210
1211
	$style = array();
1212
1213
	if ($action === 'implode')
1214
	{
1215
		$style = '';
1216
		$style_parameters = array(
1217
			'title_default_class',
1218
			'title_custom_class',
1219
			'title_custom_style',
1220
			'body_default_class',
1221
			'body_custom_class',
1222
			'body_custom_style',
1223
			'no_title',
1224
			'no_body',
1225
		);
1226
1227
		foreach ($style_parameters as $parameter)
1228
		{
1229
			if (isset($_POST[$parameter]))
1230
			{
1231
				$style .= $parameter . '~' . Util::htmlspecialchars(Util::htmltrim($_POST[$parameter]), ENT_QUOTES) . '|';
1232
			}
1233
			else
1234
			{
1235
				$style .= $parameter . '~|';
1236
			}
1237
		}
1238
1239
		if (!empty($style))
1240
		{
1241
			$style = substr($style, 0, -1);
1242
		}
1243
	}
1244
	elseif ($action === 'explode')
1245
	{
1246
		// Supplying a style set or using our defaults
1247
		if (!empty($setting))
1248
		{
1249
			$temp = explode('|', $setting);
1250
			foreach ($temp as $item)
1251
			{
1252
				list ($key, $value) = explode('~', $item);
1253
				$style[$key] = $value;
1254
			}
1255
		}
1256
		else
1257
		{
1258
			$style = array(
1259
				'title_default_class' => 'category_header',
1260
				'title_custom_class' => '',
1261
				'title_custom_style' => '',
1262
				'body_default_class' => 'portalbg',
1263
				'body_custom_class' => '',
1264
				'body_custom_style' => '',
1265
				'no_title' => false,
1266
				'no_body' => false,
1267
			);
1268
		}
1269
1270
		// Set the style values for use in the templates
1271
		if ($process && !isset($process_cache[$setting]))
1272
		{
1273
			if (empty($style['no_title']))
1274
			{
1275
				$style['title']['class'] = $style['title_default_class'];
1276
1277
				if (!empty($style['title_custom_class']))
1278
				{
1279
					$style['title']['class'] .= ' ' . $style['title_custom_class'];
1280
				}
1281
1282
				$style['title']['style'] = $style['title_custom_style'];
1283
			}
1284
1285
			if (empty($style['no_body']))
1286
			{
1287
				$style['body']['class'] = $style['body_default_class'];
1288
			}
1289
			else
1290
			{
1291
				$style['body']['class'] = '';
1292
			}
1293
1294
			if (!empty($style['body_custom_class']))
1295
			{
1296
				$style['body']['class'] .= ' ' . $style['body_custom_class'];
1297
			}
1298
1299
			$style['body']['style'] = $style['body_custom_style'];
1300
1301
			$process_cache[$setting] = $style;
1302
		}
1303
		elseif ($process)
1304
		{
1305
			$style = $process_cache[$setting];
1306
		}
1307
	}
1308
1309
	return $style;
1310
}
1311
1312
/**
1313
 * Loads a category by id, or category's by namespace
1314
 *
1315
 * @param int|string|null $category_id
1316
 * @param boolean $active
1317
 * @param boolean $allowed
1318
 * @param string $sort
1319
 *
1320
 * @return array()
1321
 */
1322
function sportal_get_categories($category_id = null, $active = false, $allowed = false, $sort = 'name')
1323
{
1324
	global $scripturl, $context;
1325
1326
	$db = database();
1327
1328
	$query = array();
1329
	$parameters = array('sort' => $sort);
1330
1331
	// Asking for a specific category or the namespace
1332
	if (!empty($category_id) && is_int($category_id))
1333
	{
1334
		$query[] = 'id_category = {int:category_id}';
1335
		$parameters['category_id'] = (int) $category_id;
1336
	}
1337
	elseif (!empty($category_id))
1338
	{
1339
		$query[] = 'namespace = {string:namespace}';
1340
		$parameters['namespace'] = $category_id;
1341
	}
1342
1343
	// Check permissions to access this category
1344
	if (!empty($allowed))
1345
	{
1346
		$query[] = sprintf($context['SPortal']['permissions']['query'], 'permissions');
1347
	}
1348
1349
	// Check if the category is even active
1350
	if (!empty($active))
1351
	{
1352
		$query[] = 'status = {int:status}';
1353
		$parameters['status'] = 1;
1354
	}
1355
1356
	// Lets see what we can find
1357
	$request = $db->query('', '
1358
		SELECT
1359
			id_category, namespace, name, description, permissions, articles, status
1360
		FROM {db_prefix}sp_categories' . (!empty($query) ? '
1361
		WHERE ' . implode(' AND ', $query) : '') . '
1362
		ORDER BY {raw:sort}', $parameters
1363
	);
1364
	$return = array();
1365
	while ($row = $db->fetch_assoc($request))
1366
	{
1367
		$return[$row['id_category']] = array(
1368
			'id' => $row['id_category'],
1369
			'category_id' => $row['namespace'],
1370
			'name' => $row['name'],
1371
			'href' => $scripturl . '?category=' . $row['namespace'],
1372
			'link' => '<a href="' . $scripturl . '?category=' . $row['namespace'] . '">' . $row['name'] . '</a>',
1373
			'description' => $row['description'],
1374
			'permissions' => $row['permissions'],
1375
			'articles' => $row['articles'],
1376
			'status' => $row['status'],
1377
		);
1378
	}
1379
	$db->free_result($request);
1380
1381
	// Return the id or the whole batch
1382
	return !empty($category_id) ? current($return) : $return;
1383
}
1384
1385
/**
1386
 * Increase the view counter for articles or pages
1387
 *
1388
 * @param string $name
1389
 * @param int $id
1390
 *
1391
 * @return bool|null
1392
 */
1393
function sportal_increase_viewcount($name, $id)
1394
{
1395
	$db = database();
1396
1397
	if ($name === 'page')
1398
	{
1399
		$query = array(
1400
			'table' => 'sp_pages',
1401
			'query_id' => 'id_page',
1402
			'id' => $id
1403
		);
1404
	}
1405
	elseif ($name === 'article')
1406
	{
1407
		$query = array(
1408
			'table' => 'sp_articles',
1409
			'query_id' => 'id_article',
1410
			'id' => $id
1411
		);
1412
	}
1413
	else
1414
	{
1415
		return false;
1416
	}
1417
1418
	$db->query('', '
1419
		UPDATE {db_prefix}{raw:table}
1420
		SET views = views + 1
1421
		WHERE {raw:query_id} = {int:id}',
1422
		array(
1423
			'table' => $query['table'],
1424
			'query_id' => $query['query_id'],
1425
			'id' => $id,
1426
		)
1427
	);
1428
1429
	return null;
1430
}
1431
1432
/**
1433
 * Load a page by ID
1434
 *
1435
 * @param int|null $page_id
1436
 * @param boolean $active
1437
 * @param boolean $allowed
1438
 * @param string $sort
1439
 *
1440
 * @return array
1441
 */
1442
function sportal_get_pages($page_id = null, $active = false, $allowed = false, $sort = 'title')
1443
{
1444
	global $context, $scripturl;
1445
	static $cache;
1446
1447
	$db = database();
1448
1449
	$page_id = is_int($page_id) || is_string($page_id) ? $page_id : 0;
1450
1451
	// If we already have the information, just return it
1452
	$cache_name = implode(':', array($page_id, $active, $allowed));
1453
	if (isset($cache[$cache_name]))
1454
	{
1455
		$return = $cache[$cache_name];
1456
	}
1457
	else
1458
	{
1459
		$query = array();
1460
		$parameters = array('sort' => $sort);
1461
1462
		// Page id or Page Namespace
1463
		if (!empty($page_id) && is_int($page_id))
1464
		{
1465
			$query[] = 'id_page = {int:page_id}';
1466
			$parameters['page_id'] = $page_id;
1467
		}
1468
		elseif (!empty($page_id))
1469
		{
1470
			$query[] = 'namespace = {string:namespace}';
1471
			$parameters['namespace'] = Util::htmlspecialchars((string) $page_id, ENT_QUOTES);
1472
		}
1473
1474
		// Use permissions?
1475
		if (!empty($allowed))
1476
		{
1477
			$query[] = sprintf($context['SPortal']['permissions']['query'], 'permissions');
1478
		}
1479
1480
		// Only active pages?
1481
		if (!empty($active))
1482
		{
1483
			$query[] = 'status = {int:status}';
1484
			$parameters['status'] = 1;
1485
		}
1486
1487
		// Make the page request
1488
		$request = $db->query('', '
1489
			SELECT
1490
				id_page, namespace, title, body, type, permissions, views, styles, status
1491
			FROM {db_prefix}sp_pages' . (!empty($query) ? '
1492
			WHERE ' . implode(' AND ', $query) : '') . '
1493
			ORDER BY {raw:sort}', $parameters
1494
		);
1495
		$return = array();
1496
		while ($row = $db->fetch_assoc($request))
1497
		{
1498
			$return[$row['id_page']] = array(
1499
				'id' => $row['id_page'],
1500
				'page_id' => $row['namespace'],
1501
				'title' => $row['title'],
1502
				'href' => $scripturl . '?page=' . $row['namespace'],
1503
				'link' => '<a href="' . $scripturl . '?page=' . $row['namespace'] . '">' . $row['title'] . '</a>',
1504
				'body' => $row['body'],
1505
				'type' => $row['type'],
1506
				'permissions' => $row['permissions'],
1507
				'views' => $row['views'],
1508
				'styles' => $row['styles'],
1509
				'status' => $row['status'],
1510
			);
1511
		}
1512
		$db->free_result($request);
1513
1514
		// Save this so we don't have to do it again
1515
		$cache[$cache_name] = $return;
1516
	}
1517
1518
	return !empty($page_id) ? current($return) : $return;
1519
}
1520
1521
/**
1522
 * Shortens the lenght of a string.
1523
 *
1524
 * What it does:
1525
 * - If the string is pure html or bcc (parsed) it will properly shorten it to that many
1526
 * characters, accounting as best it can for presentational tags etc.
1527
 * - Will use [cutoff] tag if present as primary and passed lenght second
1528
 * - If no shortening is defined (cutoff or lenght), it returns the full parsed string
1529
 *
1530
 * @param string $body
1531
 * @param string $type
1532
 * @param int $length
1533
 * @param string|null $link_id
1534
 *
1535
 * @return bool
1536
 */
1537
function sportal_parse_cutoff_content(&$body, $type, $length = 0, $link_id = null)
1538
{
1539
	global $scripturl;
1540
1541
	$cutoff = Util::strpos($body, '[cutoff]');
1542
	$cutoff = empty($cutoff) ? $length : $cutoff;
1543
1544
	// Going to shorten this string?
1545
	if (!empty($cutoff))
1546
	{
1547
		switch ($type)
1548
		{
1549
			case 'bbc':
1550
				// Protect the cutoff tag so parse_bbc does not remove it
1551
				$body = str_replace('[cutoff]', '&#91;cutoff]', $body);
1552
				$body = sportal_parse_content($body, $type, 'return');
1553
1554
				// With the bbc_parse done, determine character or marker length of plain text
1555
				$cutoff = Util::strpos(strip_tags($body), '&#91;cutoff]');
0 ignored issues
show
Bug introduced by
It seems like $body can also be of type false; however, parameter $string of strip_tags() does only seem to accept string, 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

1555
				$cutoff = Util::strpos(strip_tags(/** @scrutinizer ignore-type */ $body), '&#91;cutoff]');
Loading history...
1556
				$cutoff = empty($cutoff) ? $length : $cutoff;
1557
1558
				// Cut it at that many characters, well close anyway :D
1559
				$body = Util::shorten_html($body, $cutoff, '');
0 ignored issues
show
Bug introduced by
It seems like $body can also be of type false; however, parameter $string of Util::shorten_html() does only seem to accept string, 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

1559
				$body = Util::shorten_html(/** @scrutinizer ignore-type */ $body, $cutoff, '');
Loading history...
1560
				break;
1561
			case 'html':
1562
				// Strip tags and cut at the cutoff character count
1563
				$body = un_htmlspecialchars($body);
1564
				$body = preg_replace('~\x{00a0}~siu',' ',$body);
1565
				$cutoff = Util::strpos(strip_tags($body), '[cutoff]');
1566
				$cutoff = empty($cutoff) ? $length : $cutoff;
1567
				$body = Util::shorten_html($body, $cutoff, '');
1568
				break;
1569
			default:
1570
				$body = sportal_parse_content($body, $type, 'return');
1571
				$body = Util::substr($body, 0, $cutoff);
0 ignored issues
show
Bug introduced by
It seems like $body can also be of type false; however, parameter $string of Util::substr() does only seem to accept string, 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

1571
				$body = Util::substr(/** @scrutinizer ignore-type */ $body, 0, $cutoff);
Loading history...
1572
		}
1573
1574
		// Link the ellipsis if told
1575
		if (!empty($link_id))
1576
		{
1577
			$body .= '<a href="' . $scripturl . '?article=' . $link_id . '"><b>&hellip;</b></a>';
1578
		}
1579
	}
1580
	else
1581
	{
1582
		$body = sportal_parse_content($body, $type, 'return');
1583
	}
1584
1585
	return !empty($cutoff);
1586
}
1587
1588
/**
1589
 * Prepare body text to be of type, html, bbc, php, etc
1590
 *
1591
 * @param string $body the string of text to treat as $type
1592
 * @param string $type one of html, bbc, php
1593
 * @param string $output_method if echo will echo the results, otherwise returns the string
1594
 *
1595
 * @return string|bool
1596
 */
1597
function sportal_parse_content($body, $type, $output_method = 'echo')
1598
{
1599
	if (in_array($type, array('bbc', 'html', 'markdown')) && strpos($body, '[cutoff]') !== false)
1600
	{
1601
		$body = str_replace('[cutoff]', '', $body);
1602
	}
1603
1604
	switch ($type)
1605
	{
1606
		case 'bbc':
1607
			$parser = ParserWrapper::instance();
1608
			if ($output_method !== 'echo')
1609
			{
1610
				return $parser->parseMessage($body, true);
1611
			}
1612
1613
			echo $parser->parseMessage($body, true);
1614
			break;
1615
		case 'markdown':
1616
			require_once(EXTDIR . '/markdown/markdown.php');
1617
			if ($output_method !== 'echo')
1618
			{
1619
				return un_htmlspecialchars(Markdown($body));
1620
			}
1621
1622
			echo un_htmlspecialchars(Markdown($body));
1623
			break;
1624
		case 'html':
1625
			if ($output_method !== 'echo')
1626
			{
1627
				$body = un_htmlspecialchars($body);
1628
				return preg_replace('~\x{00a0}~su',' ',$body);
1629
			}
1630
1631
			echo un_htmlspecialchars($body);
1632
			break;
1633
		case 'php':
1634
			global $txt;
1635
1636
			$body = trim(un_htmlspecialchars($body));
1637
			$body = trim($body, '<?php');
1638
			$body = trim($body, '?>');
1639
1640
			ob_start();
1641
			try
1642
			{
1643
				eval($body);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
1644
				$result = ob_get_contents();
1645
			}
1646
			catch (\Throwable $e)
1647
			{
1648
				$result = $txt['sp_php_validation_fail'] . ', "' . $e->getMessage() . '", ' . $txt['line'] . ' ' . $e->getLine();
1649
			}
1650
			ob_end_clean();
1651
1652
			if ($output_method !== 'echo')
1653
			{
1654
				return $result;
1655
			}
1656
1657
			echo $result;
1658
			break;
1659
	}
1660
1661
	return false;
1662
}
1663
1664
/**
1665
 * Return a specific menu, or all menus if none is provided
1666
 *
1667
 * @param int|null $menu_id
1668
 * @param string $sort
1669
 *
1670
 * @return array|mixed
1671
 */
1672
function sportal_get_custom_menus($menu_id = null, $sort = 'id_menu')
1673
{
1674
	$db = database();
1675
1676
	$query = array();
1677
	$parameters = array('sort' => $sort);
1678
1679
	if (isset($menu_id))
1680
	{
1681
		$query[] = 'id_menu = {int:menu_id}';
1682
		$parameters['menu_id'] = (int) $menu_id;
1683
	}
1684
1685
	$request = $db->query('', '
1686
		SELECT
1687
			id_menu, name
1688
		FROM {db_prefix}sp_custom_menus' . (!empty($query) ? '
1689
			WHERE ' . implode(' AND ', $query) : '') . '
1690
		ORDER BY {raw:sort}',
1691
		$parameters
1692
	);
1693
	$return = array();
1694
	while ($row = $db->fetch_assoc($request))
1695
	{
1696
		$return[$row['id_menu']] = array(
1697
			'id' => $row['id_menu'],
1698
			'name' => $row['name'],
1699
		);
1700
	}
1701
	$db->free_result($request);
1702
1703
	return !empty($menu_id) ? current($return) : $return;
1704
}
1705
1706
/**
1707
 * Return menu items for a given id, or all if none is passed
1708
 *
1709
 * @param int|null $item_id
1710
 * @param string $sort
1711
 *
1712
 * @return array|mixed
1713
 */
1714
function sportal_get_menu_items($item_id = null, $sort = 'id_item')
1715
{
1716
	$db = database();
1717
1718
	$query = array();
1719
	$parameters = array('sort' => $sort);
1720
1721
	if (isset($item_id))
1722
	{
1723
		$query[] = 'id_item = {int:item_id}';
1724
		$parameters['item_id'] = (int) $item_id;
1725
	}
1726
1727
	$request = $db->query('', '
1728
		SELECT
1729
			id_item, id_menu, namespace, title, href, target
1730
		FROM {db_prefix}sp_menu_items' . (!empty($query) ? '
1731
			WHERE ' . implode(' AND ', $query) : '') . '
1732
		ORDER BY {raw:sort}',
1733
		$parameters
1734
	);
1735
	$return = array();
1736
	while ($row = $db->fetch_assoc($request))
1737
	{
1738
		$return[$row['id_item']] = array(
1739
			'id' => $row['id_item'],
1740
			'id_menu' => $row['id_menu'],
1741
			'namespace' => $row['namespace'],
1742
			'title' => $row['title'],
1743
			'url' => $row['href'],
1744
			'target' => $row['target'],
1745
		);
1746
	}
1747
	$db->free_result($request);
1748
1749
	return !empty($item_id) ? current($return) : $return;
1750
}
1751
1752
/**
1753
 * Returns the details system profiles
1754
 *
1755
 * What is does:
1756
 * - If no profile id is supplied, all profiles are returned
1757
 * - If type = 1 (generally the case), the profile group permissions are returned
1758
 * - Type 2 are Style and Type 3 are Visibility profiles
1759
 *
1760
 * @param int|null $profile_id
1761
 * @param int|null $type
1762
 * @param string|null $sort
1763
 *
1764
 * @return array
1765
 */
1766
function sportal_get_profiles($profile_id = null, $type = null, $sort = null)
1767
{
1768
	global $txt;
1769
1770
	$db = database();
1771
1772
	$query = array();
1773
	$parameters = array('sort' => $sort);
1774
1775
	if (isset($profile_id))
1776
	{
1777
		$query[] = 'id_profile = {int:profile_id}';
1778
		$parameters['profile_id'] = (int) $profile_id;
1779
	}
1780
1781
	if (isset($type))
1782
	{
1783
		$query[] = 'type = {int:type}';
1784
		$parameters['type'] = (int) $type;
1785
	}
1786
1787
	$request = $db->query('', '
1788
		SELECT
1789
			id_profile, type, name, value
1790
		FROM {db_prefix}sp_profiles' . (!empty($query) ? '
1791
		WHERE ' . implode(' AND ', $query) : '') . (!empty($sort) ? '
1792
		ORDER BY {raw:sort}' : ''),
1793
		$parameters
1794
	);
1795
	$return = array();
1796
	while ($row = $db->fetch_assoc($request))
1797
	{
1798
		$return[$row['id_profile']] = array(
1799
			'id' => $row['id_profile'],
1800
			'name' => $row['name'],
1801
			'label' => $txt['sp_admin_profiles' . substr($row['name'], 1)] ?? $row['name'],
1802
			'type' => $row['type'],
1803
			'value' => $row['value'],
1804
		);
1805
1806
		// Get the permissions
1807
		if ($row['type'] == 1)
1808
		{
1809
			list ($groups_allowed, $groups_denied) = explode('|', $row['value']);
1810
1811
			$return[$row['id_profile']] = array_merge($return[$row['id_profile']], array(
1812
				'groups_allowed' => $groups_allowed !== '' ? explode(',', $groups_allowed) : array(),
1813
				'groups_denied' => $groups_denied !== '' ? explode(',', $groups_denied) : array(),
1814
			));
1815
		}
1816
		// Styles
1817
		elseif ($row['type'] == 2)
1818
		{
1819
			$return[$row['id_profile']] = array_merge($return[$row['id_profile']], sportal_parse_style('explode', $row['value'], true));
1820
		}
1821
		// Visibility
1822
		elseif ($row['type'] == 3)
1823
		{
1824
			list ($selections, $query, $mobile_view) = array_pad(explode('|', $row['value']), 3, '');
1825
			$query = str_replace('&vert;', '|', $query);
1826
1827
			$return[$row['id_profile']] = array_merge($return[$row['id_profile']], array(
1828
				'selections' => explode(',', $selections),
1829
				'query' => $query,
1830
				'mobile_view' => $mobile_view,
1831
				'final' => implode(',', array($selections, $query, $mobile_view)),
1832
			));
1833
		}
1834
	}
1835
1836
	$db->free_result($request);
1837
1838
	return !empty($profile_id) ? current($return) : $return;
1839
}
1840
1841
/**
1842
 * Fetch a style based on its id
1843
 *
1844
 * @param int $style_id
1845
 *
1846
 * @return string
1847
 */
1848
function sportal_select_style($style_id)
1849
{
1850
	static $styles;
1851
1852
	if (!isset($styles))
1853
	{
1854
		$styles = sportal_get_profiles(null, 2);
1855
	}
1856
1857
	if (isset($styles[$style_id]))
1858
	{
1859
		return $styles[$style_id];
1860
	}
1861
	else
1862
	{
1863
		return sportal_parse_style('explode', null, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return sportal_parse_style('explode', null, true) returns the type array which is incompatible with the documented return type string.
Loading history...
1864
	}
1865
}
1866
1867
/**
1868
 * Limits the number of actions a user can do in a given time
1869
 *
1870
 * - Currently only used by the shoutboxes and article comments
1871
 *
1872
 * @param string $type
1873
 * @param boolean $fatal
1874
 *
1875
 * @return boolean
1876
 */
1877
function sp_prevent_flood($type, $fatal = true)
1878
{
1879
	global $modSettings, $user_info, $txt;
1880
1881
	$db = database();
1882
1883
	$limits = array(
1884
		'spsbp' => 5,
1885
	);
1886
1887
	if (!allowedTo('admin_forum'))
1888
	{
1889
		$time_limit = $limits[$type] ?? $modSettings['spamWaitTime'];
1890
	}
1891
	else
1892
	{
1893
		$time_limit = 2;
1894
	}
1895
1896
	// Remove old blocks
1897
	$db->query('', '
1898
		DELETE FROM {db_prefix}log_floodcontrol
1899
		WHERE log_time < {int:log_time}
1900
			AND log_type = {string:log_type}',
1901
		array(
1902
			'log_time' => time() - $time_limit,
1903
			'log_type' => $type,
1904
		)
1905
	);
1906
1907
	// Update existing ones that were still inside the time limit
1908
	$db->insert('replace', '
1909
		{db_prefix}log_floodcontrol',
1910
		array('ip' => 'string-16', 'log_time' => 'int', 'log_type' => 'string'),
1911
		array($user_info['ip'], time(), $type),
1912
		array('ip', 'log_type')
1913
	);
1914
1915
	// To many entries, need to slow them down
1916
	if ($db->affected_rows() != 1)
1917
	{
1918
		if ($fatal)
1919
		{
1920
			throw new Elk_Exception('error_sp_flood_' . $type, false, array($time_limit));
1921
		}
1922
		else
1923
		{
1924
			return isset($txt['error_sp_flood_' . $type]) ? sprintf($txt['error_sp_flood_' . $type], $time_limit) : true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? sprin...e], $time_limit) : true also could return the type string which is incompatible with the documented return type boolean.
Loading history...
1925
		}
1926
	}
1927
1928
	return false;
1929
}
1930