Completed
Pull Request — patch_1-1-4 (#3191)
by Emanuele
13:25
created

Theme::loadDefaultLayers()   C

Complexity

Conditions 8
Paths 18

Size

Total Lines 67
Code Lines 32

Duplication

Lines 4
Ratio 5.97 %

Importance

Changes 0
Metric Value
cc 8
eloc 32
nc 18
nop 0
dl 4
loc 67
rs 6.6523
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
 * The default theme
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:    2011 Simple Machines (http://www.simplemachines.org)
12
 * license:      BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1.2
15
 *
16
 */
17
18
namespace Themes\DefaultTheme;
19
20
/**
21
 * Class Theme
22
 *
23
 * - Extends the abstract theme class
24
 *
25
 * @package Themes\DefaultTheme
26
 */
27
class Theme extends \Theme
28
{
29
	/**
30
	 * This is the only template included in the sources.
31
	 */
32
	public function template_rawdata()
33
	{
34
		global $context;
35
36
		echo $context['raw_data'];
37
	}
38
39
	/**
40
	 * The header template
41
	 */
42
	public function template_header()
43
	{
44
		global $context, $settings;
45
46
		doSecurityChecks();
47
48
		$this->setupThemeContext();
49
50
		// Print stuff to prevent caching of pages (except on attachment errors, etc.)
51
		if (empty($context['no_last_modified']))
52
		{
53
			header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
54
			header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
55
56
			if ($this->headerSent('Content-Type') === false && (!isset($_REQUEST['xml']) && !isset($_REQUEST['api'])))
57
			{
58
				header('Content-Type: text/html; charset=UTF-8');
59
			}
60
		}
61
62
		if ($this->headerSent('Content-Type') === false)
63
		{
64
			// Probably temporary ($_REQUEST['xml'] should be replaced by $_REQUEST['api'])
65
			if (isset($_REQUEST['api']) && $_REQUEST['api'] === 'json')
66
			{
67
				header('Content-Type: application/json; charset=UTF-8');
68
			}
69
			elseif (isset($_REQUEST['xml']) || isset($_REQUEST['api']))
70
			{
71
				header('Content-Type: text/xml; charset=UTF-8');
72
			}
73
			else
74
			{
75
				header('Content-Type: text/html; charset=UTF-8');
76
			}
77
		}
78
79
		foreach ($this->layers->prepareContext() as $layer)
80
		{
81
			loadSubTemplate($layer . '_above', 'ignore');
82
		}
83
84 View Code Duplication
		if (isset($settings['use_default_images']) && $settings['use_default_images'] === 'defaults' && isset($settings['default_template']))
85
		{
86
			$settings['theme_url'] = $settings['default_theme_url'];
87
			$settings['images_url'] = $settings['default_images_url'];
88
			$settings['theme_dir'] = $settings['default_theme_dir'];
89
		}
90
	}
91
92
	/**
93
	 * Checks if a header of a given type has already been sent
94
	 *
95
	 * @param string $type
96
	 *
97
	 * @return bool
98
	 */
99
	protected function headerSent($type)
100
	{
101
		$sent = headers_list();
102
		foreach ($sent as $header)
103
		{
104
			if (substr($header, 0, strlen($type)) === $type)
105
			{
106
				return true;
107
			}
108
		}
109
110
		return false;
111
	}
112
113
	/**
114
	 * Show the copyright.
115
	 */
116
	public function theme_copyright()
117
	{
118
		global $forum_copyright;
119
120
		// Don't display copyright for things like SSI.
121
		if (!defined('FORUM_VERSION'))
122
		{
123
			return;
124
		}
125
126
		// Put in the version...
127
		$forum_copyright = replaceBasicActionUrl(sprintf($forum_copyright, FORUM_VERSION));
128
129
		echo '
130
					', $forum_copyright;
131
	}
132
133
	/**
134
	 * The template footer
135
	 */
136
	public function template_footer()
137
	{
138
		global $context, $settings, $modSettings, $time_start;
139
140
		$db = database();
141
142
		// Show the load time?  (only makes sense for the footer.)
143
		$context['show_load_time'] = !empty($modSettings['timeLoadPageEnable']);
144
		$context['load_time'] = round(microtime(true) - $time_start, 3);
145
		$context['load_queries'] = $db->num_queries();
146
147 View Code Duplication
		if (isset($settings['use_default_images']) && $settings['use_default_images'] === 'defaults' && isset($settings['default_template']))
148
		{
149
			$settings['theme_url'] = $settings['actual_theme_url'];
150
			$settings['images_url'] = $settings['actual_images_url'];
151
			$settings['theme_dir'] = $settings['actual_theme_dir'];
152
		}
153
154
		foreach ($this->layers->reverseLayers() as $layer)
155
		{
156
			$this->templates->loadSubTemplate($layer . '_below', 'ignore');
157
		}
158
	}
159
160
	/**
161
	 * Loads the required jQuery files for the system
162
	 *
163
	 * - Determines the correct script tags to add based on CDN/Local/Auto
164
	 */
165
	protected function templateJquery()
166
	{
167
		global $modSettings, $settings;
168
169
		// Using a specified version of jquery or what was shipped 3.1.1  / 1.12.1
170
		$jquery_version = (!empty($modSettings['jquery_default']) && !empty($modSettings['jquery_version'])) ? $modSettings['jquery_version'] : '3.1.1';
171
		$jqueryui_version = (!empty($modSettings['jqueryui_default']) && !empty($modSettings['jqueryui_version'])) ? $modSettings['jqueryui_version'] : '1.12.1';
172
173
		switch ($modSettings['jquery_source'])
174
		{
175
			// Only getting the files from the CDN?
176
			case 'cdn':
177
				echo '
178
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/' . $jquery_version . '/jquery.min.js" id="jquery"></script>',
179
				(!empty($modSettings['jquery_include_ui']) ? '
180
	<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/' . $jqueryui_version . '/jquery-ui.min.js" id="jqueryui"></script>' : '');
181
				break;
182
			// Just use the local file
183
			case 'local':
184
				echo '
185
	<script src="', $settings['default_theme_url'], '/scripts/jquery-' . $jquery_version . '.min.js" id="jquery"></script>',
186
				(!empty($modSettings['jquery_include_ui']) ? '
187
	<script src="' . $settings['default_theme_url'] . '/scripts/jquery-ui-' . $jqueryui_version . '.min.js" id="jqueryui"></script>' : '');
188
				break;
189
			// CDN with local fallback
190
			case 'auto':
191
				echo '
192
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/' . $jquery_version . '/jquery.min.js" id="jquery"></script>',
193
				(!empty($modSettings['jquery_include_ui']) ? '
194
	<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/' . $jqueryui_version . '/jquery-ui.min.js" id="jqueryui"></script>' : '');
195
				echo '
196
	<script>
197
		window.jQuery || document.write(\'<script src="', $settings['default_theme_url'], '/scripts/jquery-' . $jquery_version . '.min.js"><\/script>\');',
198
				(!empty($modSettings['jquery_include_ui']) ? '
199
		window.jQuery.ui || document.write(\'<script src="' . $settings['default_theme_url'] . '/scripts/jquery-ui-' . $jqueryui_version . '.min.js"><\/script>\')' : ''), '
200
	</script>';
201
				break;
202
		}
203
	}
204
205
	/**
206
	 * Loads the JS files that have been requested
207
	 *
208
	 * - Will combine / minify the files it the option is set.
209
	 * - Handles both above and below (deferred) files
210
	 *
211
	 * @param bool $do_deferred
212
	 */
213
	protected function templateJavascriptFiles($do_deferred)
214
	{
215
		global $modSettings, $settings;
216
217
		// Combine and minify javascript source files to save bandwidth and requests
218
		if (!empty($modSettings['minify_css_js']))
219
		{
220
			$combiner = new \Site_Combiner($settings['default_theme_cache_dir'], $settings['default_theme_cache_url']);
221
			$combine_name = $combiner->site_js_combine($this->js_files, $do_deferred);
222
223
			call_integration_hook('post_javascript_combine', array(&$combine_name, $combiner));
224
225
			if (!empty($combine_name))
226
			{
227
				echo '
228
	<script src="', $combine_name, '" id="jscombined', $do_deferred ? 'bottom' : 'top', '"></script>';
229
			}
230
			// While we have Javascript files to place in the template
231
			foreach ($combiner->getSpares() as $id => $js_file)
232
			{
233
				if ((!$do_deferred && empty($js_file['options']['defer'])) || ($do_deferred && !empty($js_file['options']['defer'])))
234
				{
235
					echo '
236
	<script src="', $js_file['filename'], '" id="', $id, '"', !empty($js_file['options']['async']) ? ' async="async"' : '', '></script>';
237
				}
238
			}
239
		}
240
		// Just give them the full load then
241
		else
242
		{
243
			// While we have Javascript files to place in the template
244
			foreach ($this->js_files as $id => $js_file)
245
			{
246
				if ((!$do_deferred && empty($js_file['options']['defer'])) || ($do_deferred && !empty($js_file['options']['defer'])))
247
				{
248
					echo '
249
	<script src="', $js_file['filename'], '" id="', $id, '"', !empty($js_file['options']['async']) ? ' async="async"' : '', '></script>';
250
				}
251
			}
252
		}
253
	}
254
255
	/**
256
	 * Deletes the hives (aggregated CSS and JS files) previously created.
257
	 *
258
	 * @param string $type = 'all' Filters the types of hives (valid values:
259
	 *                               * 'all'
260
	 *                               * 'css'
261
	 *                               * 'js'
262
	 */
263
	public function cleanHives($type = 'all')
264
	{
265
		global $settings;
266
267
		$combiner = new \Site_Combiner($settings['default_theme_cache_dir'], $settings['default_theme_cache_url']);
268
		$result = true;
269
270
		if ($type === 'all' || $type === 'css')
271
		{
272
			$result &= $combiner->removeCssHives();
273
		}
274
275
		if ($type === 'all' || $type === 'js')
276
		{
277
			$result &= $combiner->removeJsHives();
278
		}
279
280
		return $result;
281
	}
282
283
	/**
284
	 * Output the Javascript files
285
	 *
286
	 * What it does:
287
	 *
288
	 * - Tabbing in this function is to make the HTML source look proper
289
	 * - Outputs jQuery/jQueryUI from the proper source (local/CDN)
290
	 * - If deferred is set function will output all JS (source & inline) set to load at page end
291
	 * - If the admin option to combine files is set, will use Combiner.class
292
	 *
293
	 * @param bool $do_deferred = false
294
	 */
295
	public function template_javascript($do_deferred = false)
296
	{
297
		global $modSettings;
298
299
		// First up, load jQuery and jQuery UI
300
		if (isset($modSettings['jquery_source']) && !$do_deferred)
301
		{
302
			$this->templateJquery();
303
		}
304
305
		// Use this hook to work with Javascript files and vars pre output
306
		call_integration_hook('pre_javascript_output', array($do_deferred));
307
308
		// Load in the JS files
309
		if (!empty($this->js_files))
310
		{
311
			$this->templateJavascriptFiles($do_deferred);
312
		}
313
314
		// Build the declared Javascript variables script
315
		$js_vars = array();
316
		if (!empty($this->js_vars) && !$do_deferred)
317
		{
318
			foreach ($this->js_vars as $var => $value)
319
				$js_vars[] = $var . ' = ' . $value;
320
321
			// Newlines and tabs are here to make it look nice in the page source view, stripped if minimized though
322
			$this->js_inline['standard'][] = 'var ' . implode(",\n\t\t\t", $js_vars) . ';';
323
		}
324
325
		// Inline JavaScript - Actually useful some times!
326
		if (!empty($this->js_inline))
327
		{
328
			// Deferred output waits until we are deferring !
329 View Code Duplication
			if (!empty($this->js_inline['defer']) && $do_deferred)
330
			{
331
				// Combine them all in to one output
332
				$this->js_inline['defer'] = array_map('trim', $this->js_inline['defer']);
333
				$inline_defered_code = implode("\n\t\t", $this->js_inline['defer']);
334
335
				// Output the deferred script
336
				echo '
337
	<script>
338
		', $inline_defered_code, '
339
	</script>';
340
			}
341
342
			// Standard output, and our javascript vars, get output when we are not on a defered call
343 View Code Duplication
			if (!empty($this->js_inline['standard']) && !$do_deferred)
344
			{
345
				$this->js_inline['standard'] = array_map('trim', $this->js_inline['standard']);
346
347
				// And output the js vars and standard scripts to the page
348
				echo '
349
	<script>
350
		', implode("\n\t\t", $this->js_inline['standard']), '
351
	</script>';
352
			}
353
		}
354
	}
355
356
	/**
357
	 * Output the CSS files
358
	 *
359
	 * What it does:
360
	 *  - If the admin option to combine files is set, will use Combiner.class
361
	 */
362
	public function template_css()
363
	{
364
		global $modSettings, $settings;
365
366
		// Use this hook to work with CSS files pre output
367
		call_integration_hook('pre_css_output');
368
369
		// Combine and minify the CSS files to save bandwidth and requests?
370
		if (!empty($this->css_files))
371
		{
372
			if (!empty($modSettings['minify_css_js']))
373
			{
374
				$combiner = new \Site_Combiner($settings['default_theme_cache_dir'], $settings['default_theme_cache_url']);
375
				$combine_name = $combiner->site_css_combine($this->css_files);
376
377
				call_integration_hook('post_css_combine', array(&$combine_name, $combiner));
378
379
				if (!empty($combine_name))
380
				{
381
					echo '
382
	<link rel="stylesheet" href="', $combine_name, '" id="csscombined" />';
383
				}
384
385
				foreach ($combiner->getSpares() as $id => $file)
386
					echo '
387
	<link rel="stylesheet" href="', $file['filename'], '" id="', $id, '" />';
388
			}
389
			else
390
			{
391
				foreach ($this->css_files as $id => $file)
392
					echo '
393
	<link rel="stylesheet" href="', $file['filename'], '" id="', $id, '" />';
394
			}
395
		}
396
	}
397
398
	/**
399
	 * Output the inline-CSS in a style tag
400
	 */
401
	public function template_inlinecss()
402
	{
403
		$style_tag = '';
404
405
		// Combine and minify the CSS files to save bandwidth and requests?
406
		if (!empty($this->css_rules))
407
		{
408
			if (!empty($this->css_rules['all']))
409
			{
410
				$style_tag .= '
411
	' . $this->css_rules['all'];
412
			}
413
			if (!empty($this->css_rules['media']))
414
			{
415
				foreach ($this->css_rules['media'] as $key => $val)
416
				{
417
					$style_tag .= '
418
	@media ' . $key . '{
419
		' . $val . '
420
	}';
421
				}
422
			}
423
		}
424
425
		if (!empty($style_tag))
426
		{
427
			echo '
428
	<style>' . $style_tag . '
429
	</style>';
430
		}
431
	}
432
433
	/**
434
	 * Calls on template_show_error from index.template.php to show warnings
435
	 * and security errors for admins
436
	 */
437
	public function template_admin_warning_above()
438
	{
439
		global $context, $txt;
440
441 View Code Duplication
		if (!empty($context['security_controls_files']))
442
		{
443
			$context['security_controls_files']['type'] = 'serious';
444
			template_show_error('security_controls_files');
445
		}
446
447 View Code Duplication
		if (!empty($context['security_controls_query']))
448
		{
449
			$context['security_controls_query']['type'] = 'serious';
450
			template_show_error('security_controls_query');
451
		}
452
453 View Code Duplication
		if (!empty($context['security_controls_ban']))
454
		{
455
			$context['security_controls_ban']['type'] = 'serious';
456
			template_show_error('security_controls_ban');
457
		}
458
459
		if (!empty($context['new_version_updates']))
460
		{
461
			template_show_error('new_version_updates');
462
		}
463
464
		if (!empty($context['accepted_agreement']))
465
		{
466
			template_show_error('accepted_agreement');
467
		}
468
469
		// Any special notices to remind the admin about?
470
		if (!empty($context['warning_controls']))
471
		{
472
			$context['warning_controls']['errors'] = $context['warning_controls'];
473
			$context['warning_controls']['title'] = $txt['admin_warning_title'];
474
			$context['warning_controls']['type'] = 'warning';
475
			template_show_error('warning_controls');
476
		}
477
	}
478
479
	/**
480
	 * If the option to pretty output code is on, this loads the JS and CSS
481
	 */
482
	public function addCodePrettify()
483
	{
484
		global $modSettings;
485
486
		if (!empty($modSettings['enableCodePrettify']))
487
		{
488
			loadCSSFile('prettify.css');
489
			loadJavascriptFile('prettify.min.js', array('defer' => true));
490
491
			addInlineJavascript('
492
			$(function() {
493
				prettyPrint();
494
			});', true);
495
		}
496
	}
497
498
	/**
499
	 * If video embedding is enabled, this loads the needed JS and vars
500
	 */
501
	public function autoEmbedVideo()
502
	{
503
		global $txt, $modSettings;
504
505
		if (!empty($modSettings['enableVideoEmbeding']))
506
		{
507
			addInlineJavascript('
508
			var oEmbedtext = ({
509
				preview_image : ' . JavaScriptEscape($txt['preview_image']) . ',
510
				ctp_video : ' . JavaScriptEscape($txt['ctp_video']) . ',
511
				hide_video : ' . JavaScriptEscape($txt['hide_video']) . ',
512
				youtube : ' . JavaScriptEscape($txt['youtube']) . ',
513
				vimeo : ' . JavaScriptEscape($txt['vimeo']) . ',
514
				dailymotion : ' . JavaScriptEscape($txt['dailymotion']) . '
515
			});', true);
516
517
			loadJavascriptFile('elk_jquery_embed.js', array('defer' => true));
518
		}
519
	}
520
521
	/**
522
	 * Ensures we kick the mail queue from time to time so that it gets
523
	 * checked as often as possible.
524
	 */
525
	public function doScheduledSendMail()
526
	{
527
		global $modSettings;
528
529
		if (isBrowser('possibly_robot'))
530
		{
531
			// @todo Maybe move this somewhere better?!
532
			$controller = new \ScheduledTasks_Controller();
533
534
			// What to do, what to do?!
535
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
536
			{
537
				$controller->action_autotask();
538
			}
539
			else
540
			{
541
				$controller->action_reducemailqueue();
542
			}
543
		}
544
		else
545
		{
546
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
547
			$ts = $type === 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
548
549
			addInlineJavascript('
550
		function elkAutoTask()
551
		{
552
			var tempImage = new Image();
553
			tempImage.src = elk_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '";
554
		}
555
		window.setTimeout("elkAutoTask();", 1);', true);
556
		}
557
	}
558
559
	/**
560
	 * Relative times require a few variables be set in the JS
561
	 */
562
	public function relativeTimes()
563
	{
564
		global $modSettings, $context, $txt;
565
566
		// Relative times?
567
		if (!empty($modSettings['todayMod']) && $modSettings['todayMod'] > 2)
568
		{
569
			addInlineJavascript('
570
			var oRttime = ({
571
				referenceTime : ' . forum_time() * 1000 . ',
572
				now : ' . JavaScriptEscape($txt['rt_now']) . ',
573
				minute : ' . JavaScriptEscape($txt['rt_minute']) . ',
574
				minutes : ' . JavaScriptEscape($txt['rt_minutes']) . ',
575
				hour : ' . JavaScriptEscape($txt['rt_hour']) . ',
576
				hours : ' . JavaScriptEscape($txt['rt_hours']) . ',
577
				day : ' . JavaScriptEscape($txt['rt_day']) . ',
578
				days : ' . JavaScriptEscape($txt['rt_days']) . ',
579
				week : ' . JavaScriptEscape($txt['rt_week']) . ',
580
				weeks : ' . JavaScriptEscape($txt['rt_weeks']) . ',
581
				month : ' . JavaScriptEscape($txt['rt_month']) . ',
582
				months : ' . JavaScriptEscape($txt['rt_months']) . ',
583
				year : ' . JavaScriptEscape($txt['rt_year']) . ',
584
				years : ' . JavaScriptEscape($txt['rt_years']) . ',
585
			});
586
			updateRelativeTime();', true);
587
588
			$context['using_relative_time'] = true;
589
		}
590
	}
591
592
	/**
593
	 * Sets up the basic theme context stuff.
594
	 *
595
	 * @param bool $forceload = false
596
	 */
597
	public function setupThemeContext($forceload = false)
598
	{
599
		global $modSettings, $user_info, $scripturl, $context, $settings, $options, $txt;
600
601
		static $loaded = false;
602
603
		// Under SSI this function can be called more then once.  That can cause some problems.
604
		// So only run the function once unless we are forced to run it again.
605
		if ($loaded && !$forceload)
606
		{
607
			return;
608
		}
609
610
		$loaded = true;
611
612
		$context['current_time'] = standardTime(time(), false);
613
		$context['current_action'] = isset($_GET['action']) ? $_GET['action'] : '';
614
		$context['show_quick_login'] = !empty($modSettings['enableVBStyleLogin']) && $user_info['is_guest'];
615
		$context['robot_no_index'] = in_array($context['current_action'], $this->no_index_actions);
616
617
		$bbc_parser = \BBC\ParserWrapper::instance();
618
619
		// Get some news...
620
		$context['news_lines'] = array_filter(explode("\n", str_replace("\r", '', trim(addslashes($modSettings['news'])))));
621
		for ($i = 0, $n = count($context['news_lines']); $i < $n; $i++)
622
		{
623
			if (trim($context['news_lines'][$i]) === '')
624
			{
625
				continue;
626
			}
627
628
			// Clean it up for presentation ;).
629
			$context['news_lines'][$i] = $bbc_parser->parseNews(stripslashes(trim($context['news_lines'][$i])));
630
		}
631
632
		// If we have some, setup for display
633
		if (!empty($context['news_lines']))
634
		{
635
			$context['random_news_line'] = $context['news_lines'][mt_rand(0, count($context['news_lines']) - 1)];
636
			$context['upper_content_callbacks'][] = 'news_fader';
637
		}
638
639
		if (!$user_info['is_guest'])
640
		{
641
			$context['user']['messages'] = &$user_info['messages'];
642
			$context['user']['unread_messages'] = &$user_info['unread_messages'];
643
			$context['user']['mentions'] = &$user_info['mentions'];
644
645
			// Personal message popup...
646
			if ($user_info['unread_messages'] > (isset($_SESSION['unread_messages']) ? $_SESSION['unread_messages'] : 0))
647
			{
648
				$context['user']['popup_messages'] = true;
649
			}
650
			else
651
			{
652
				$context['user']['popup_messages'] = false;
653
			}
654
655
			$_SESSION['unread_messages'] = $user_info['unread_messages'];
656
657
			$context['user']['avatar'] = array(
658
				'href' => !empty($user_info['avatar']['href']) ? $user_info['avatar']['href'] : '',
659
				'image' => !empty($user_info['avatar']['image']) ? $user_info['avatar']['image'] : '',
660
			);
661
662
			// Figure out how long they've been logged in.
663
			$context['user']['total_time_logged_in'] = array(
664
				'days' => floor($user_info['total_time_logged_in'] / 86400),
665
				'hours' => floor(($user_info['total_time_logged_in'] % 86400) / 3600),
666
				'minutes' => floor(($user_info['total_time_logged_in'] % 3600) / 60)
667
			);
668
		}
669
		else
670
		{
671
			$context['user']['messages'] = 0;
672
			$context['user']['unread_messages'] = 0;
673
			$context['user']['mentions'] = 0;
674
			$context['user']['avatar'] = array();
675
			$context['user']['total_time_logged_in'] = array('days' => 0, 'hours' => 0, 'minutes' => 0);
676
			$context['user']['popup_messages'] = false;
677
678
			if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1)
679
			{
680
				$txt['welcome_guest'] .= $txt['welcome_guest_activate'];
681
			}
682
683
			$txt['welcome_guest'] = replaceBasicActionUrl($txt['welcome_guest']);
684
685
			// If we've upgraded recently, go easy on the passwords.
686
			if (!empty($modSettings['enable_password_conversion']))
687
			{
688
				$context['disable_login_hashing'] = true;
689
			}
690
		}
691
692
		// Setup the main menu items.
693
		$this->setupMenuContext();
694
695
		if (empty($settings['theme_version']))
696
		{
697
			$context['show_vBlogin'] = $context['show_quick_login'];
698
		}
699
700
		// This is here because old index templates might still use it.
701
		$context['show_news'] = !empty($settings['enable_news']);
702
703
		$context['additional_dropdown_search'] = prepareSearchEngines();
704
705
		// This is done to allow theme authors to customize it as they want.
706
		$context['show_pm_popup'] = $context['user']['popup_messages'] && !empty($options['popup_messages']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] !== 'pm');
707
708
		// Add the PM popup here instead. Theme authors can still override it simply by editing/removing the 'fPmPopup' in the array.
709
		if ($context['show_pm_popup'])
710
		{
711
			addInlineJavascript('
712
			$(function() {
713
				new smc_Popup({
714
					heading: ' . JavaScriptEscape($txt['show_personal_messages_heading']) . ',
715
					content: ' . JavaScriptEscape(sprintf($txt['show_personal_messages'], $context['user']['unread_messages'], $scripturl . '?action=pm')) . ',
716
					icon: \'i-envelope\'
717
				});
718
			});', true);
719
		}
720
721
		// This looks weird, but it's because BoardIndex.controller.php references the variable.
722
		$context['common_stats']['latest_member'] = array(
723
			'id' => $modSettings['latestMember'],
724
			'name' => $modSettings['latestRealName'],
725
			'href' => $scripturl . '?action=profile;u=' . $modSettings['latestMember'],
726
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $modSettings['latestRealName'] . '</a>',
727
		);
728
729
		$context['common_stats'] = array(
730
			'total_posts' => comma_format($modSettings['totalMessages']),
731
			'total_topics' => comma_format($modSettings['totalTopics']),
732
			'total_members' => comma_format($modSettings['totalMembers']),
733
			'latest_member' => $context['common_stats']['latest_member'],
734
		);
735
736
		$context['common_stats']['boardindex_total_posts'] = sprintf($txt['boardindex_total_posts'], $context['common_stats']['total_posts'], $context['common_stats']['total_topics'], $context['common_stats']['total_members']);
737
738
		if (empty($settings['theme_version']))
739
		{
740
			addJavascriptVar(array('elk_scripturl' => '\'' . $scripturl . '\''));
741
		}
742
		addJavascriptVar(array('elk_forum_action' => '\'' . substr($modSettings['default_forum_action'], 1, -1) . '\''));
743
744
		if (!isset($context['page_title']))
745
		{
746
			$context['page_title'] = '';
747
		}
748
749
		// Set some specific vars.
750
		$context['page_title_html_safe'] = \Util::htmlspecialchars(un_htmlspecialchars($context['page_title'])) . (!empty($context['current_page']) ? ' - ' . $txt['page'] . ' ' . ($context['current_page'] + 1) : '');
751
752
		// 1.0 backward compatibility: if you already put the icon in the theme dir
753
		// use that one, otherwise the default
754
		// @deprecated since 1.1
755
		if (file_exists(BOARDDIR . '/mobile.png'))
756
		{
757
			$context['favicon'] = $scripturl . '/mobile.png';
758
		}
759
		else
760
		{
761
			$context['favicon'] = $settings['images_url'] . '/mobile.png';
762
		}
763
764
		// Since it's nice to have avatars all of the same size, and in some cases the size detection may fail,
765
		// let's add the css in any case
766
		if (!isset($context['html_headers']))
767
			$context['html_headers'] = '';
768
769
		if (!empty($modSettings['avatar_max_width']) || !empty($modSettings['avatar_max_height']))
770
		{
771
			$this->addCSSRules('
772
		.avatarresize {' . (!empty($modSettings['avatar_max_width']) ? '
773
			max-width:' . $modSettings['avatar_max_width'] . 'px;' : '') . (!empty($modSettings['avatar_max_height']) ? '
774
			max-height:' . $modSettings['avatar_max_height'] . 'px;' : '') . '
775
		}');
776
		}
777
778
		// Save some database hits, if a width for multiple wrappers is set in admin.
779
		if (!empty($settings['forum_width']))
780
		{
781
			$this->addCSSRules('
782
		.wrapper {width: ' . $settings['forum_width'] . ';}');
783
		}
784
	}
785
786
	/**
787
	 * Adds required support CSS files.
788
	 */
789
	public function loadSupportCSS()
790
	{
791
		global $modSettings, $settings;
792
793
		// Load the SVG support file with fallback to default theme
794
		loadCSSFile('icons_svg.css');
795
796
		// Load a base theme custom CSS file?
797
		if (file_exists($settings['theme_dir'] . '/css/custom.css'))
798
		{
799
			loadCSSFile('custom.css', array('fallback' => false));
800
		}
801
802
		// Load font Awesome fonts, @deprecated in 1.1 and will be removed in 2.0
803
		if (!empty($settings['require_font-awesome']) || !empty($modSettings['require_font-awesome']))
804
		{
805
			loadCSSFile('font-awesome.min.css');
806
		}
807
	}
808
809
	/**
810
	 * Sets up all of the top menu buttons
811
	 *
812
	 * What it does:
813
	 *
814
	 * - Defines every master item in the menu, as well as any sub-items
815
	 * - Ensures the chosen action is set so the menu is highlighted
816
	 * - Saves them in the cache if it is available and on
817
	 * - Places the results in $context
818
	 */
819
	public function setupMenuContext()
820
	{
821
		global $context, $modSettings, $user_info, $settings;
822
823
		// Set up the menu privileges.
824
		$context['allow_search'] = !empty($modSettings['allow_guestAccess']) ? allowedTo('search_posts') : (!$user_info['is_guest'] && allowedTo('search_posts'));
825
		$context['allow_admin'] = allowedTo(array('admin_forum', 'manage_boards', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_attachments', 'manage_smileys'));
826
		$context['allow_edit_profile'] = !$user_info['is_guest'] && allowedTo(array('profile_view_own', 'profile_view_any', 'profile_identity_own', 'profile_identity_any', 'profile_extra_own', 'profile_extra_any', 'profile_remove_own', 'profile_remove_any', 'moderate_forum', 'manage_membergroups', 'profile_title_own', 'profile_title_any'));
827
		$context['allow_memberlist'] = allowedTo('view_mlist');
828
		$context['allow_calendar'] = allowedTo('calendar_view') && !empty($modSettings['cal_enabled']);
829
		$context['allow_moderation_center'] = $context['user']['can_mod'];
830
		$context['allow_pm'] = allowedTo('pm_read');
831
832
		call_integration_hook('integrate_setup_allow');
833
834
		if ($context['allow_search'])
835
		{
836
			$context['theme_header_callbacks'] = elk_array_insert($context['theme_header_callbacks'], 'login_bar', array('search_bar'), 'after');
837
		}
838
839
		$cacheTime = $modSettings['lastActive'] * 60;
840
841
		// Update the Moderation menu items with action item totals
842
		if ($context['allow_moderation_center'])
843
		{
844
			// Get the numbers for the menu ...
845
			require_once(SUBSDIR . '/Moderation.subs.php');
846
			$menu_count = loadModeratorMenuCounts();
847
		}
848
849
		$menu_count['unread_messages'] = $context['user']['unread_messages'];
0 ignored issues
show
Bug introduced by
The variable $menu_count does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
850
		$menu_count['mentions'] = $context['user']['mentions'];
851
852
		if (!empty($user_info['avatar']['href']))
853
		{
854
			$this->addCSSRules('
855
	.i-account:before {
856
		content: "";
857
		background-image: url("' . $user_info['avatar']['href'] . '");
858
	}');
859
		}
860
861
		// All the buttons we can possible want and then some, try pulling the final list of buttons from cache first.
862
		if (($menu_buttons = cache_get_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $cacheTime)) === null || time() - $cacheTime <= $modSettings['settings_updated'])
863
		{
864
			// Start things up: this is what we know by default
865
			require_once(SUBSDIR . '/Menu.subs.php');
866
			$buttons = loadDefaultMenuButtons();
867
868
			// Allow editing menu buttons easily.
869
			call_integration_hook('integrate_menu_buttons', array(&$buttons, &$menu_count));
870
871
			// Now we put the buttons in the context so the theme can use them.
872
			$menu_buttons = array();
873
			foreach ($buttons as $act => $button)
874
			{
875
				if (!empty($button['show']))
876
				{
877
					$button['active_button'] = false;
878
879
					// This button needs some action.
880
					if (isset($button['action_hook']))
881
					{
882
						$needs_action_hook = true;
883
					}
884
885
					if (isset($button['counter']) && !empty($menu_count[$button['counter']]))
886
					{
887
						$button['alttitle'] = $button['title'] . ' [' . $menu_count[$button['counter']] . ']';
888 View Code Duplication
						if (!empty($settings['menu_numeric_notice'][0]))
889
						{
890
							$button['title'] .= sprintf($settings['menu_numeric_notice'][0], $menu_count[$button['counter']]);
891
							$button['indicator'] = true;
892
						}
893
					}
894
895
					// Go through the sub buttons if there are any.
896
					if (isset($button['sub_buttons']))
897
					{
898
						foreach ($button['sub_buttons'] as $key => $subbutton)
899
						{
900
							if (empty($subbutton['show']))
901
							{
902
								unset($button['sub_buttons'][$key]);
903
							}
904
							elseif (isset($subbutton['counter']) && !empty($menu_count[$subbutton['counter']]))
905
							{
906
								$button['sub_buttons'][$key]['alttitle'] = $subbutton['title'] . ' [' . $menu_count[$subbutton['counter']] . ']';
907 View Code Duplication
								if (!empty($settings['menu_numeric_notice'][1]))
908
								{
909
									$button['sub_buttons'][$key]['title'] .= sprintf($settings['menu_numeric_notice'][1], $menu_count[$subbutton['counter']]);
910
								}
911
912
								// 2nd level sub buttons next...
913
								if (isset($subbutton['sub_buttons']))
914
								{
915
									foreach ($subbutton['sub_buttons'] as $key2 => $subbutton2)
916
									{
917
										$button['sub_buttons'][$key]['sub_buttons'][$key2] = $subbutton2;
918
										if (empty($subbutton2['show']))
919
										{
920
											unset($button['sub_buttons'][$key]['sub_buttons'][$key2]);
921
										}
922
										elseif (isset($subbutton2['counter']) && !empty($menu_count[$subbutton2['counter']]))
923
										{
924
											$button['sub_buttons'][$key]['sub_buttons'][$key2]['alttitle'] = $subbutton2['title'] . ' [' . $menu_count[$subbutton2['counter']] . ']';
925
											if (!empty($settings['menu_numeric_notice'][2]))
926
											{
927
												$button['sub_buttons'][$key]['sub_buttons'][$key2]['title'] .= sprintf($settings['menu_numeric_notice'][2], $menu_count[$subbutton2['counter']]);
928
											}
929
											unset($menu_count[$subbutton2['counter']]);
930
										}
931
									}
932
								}
933
							}
934
						}
935
					}
936
937
					$menu_buttons[$act] = $button;
938
				}
939
			}
940
941
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
942
			{
943
				cache_put_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $menu_buttons, $cacheTime);
944
			}
945
		}
946
947
		if (!empty($menu_buttons['profile']['sub_buttons']['logout']))
948
		{
949
			$menu_buttons['profile']['sub_buttons']['logout']['href'] .= ';' . $context['session_var'] . '=' . $context['session_id'];
950
		}
951
952
		$context['menu_buttons'] = $menu_buttons;
953
954
		// Figure out which action we are doing so we can set the active tab.
955
		// Default to home.
956
		$current_action = 'home';
957
958
		if (isset($context['menu_buttons'][$context['current_action']]))
959
		{
960
			$current_action = $context['current_action'];
961
		}
962
		elseif ($context['current_action'] === 'profile')
963
		{
964
			$current_action = 'pm';
965
		}
966
		elseif ($context['current_action'] === 'theme')
967
		{
968
			$current_action = isset($_REQUEST['sa']) && $_REQUEST['sa'] === 'pick' ? 'profile' : 'admin';
969
		}
970
		elseif ($context['current_action'] === 'login2' || ($user_info['is_guest'] && $context['current_action'] === 'reminder'))
971
		{
972
			$current_action = 'login';
973
		}
974
		elseif ($context['current_action'] === 'groups' && $context['allow_moderation_center'])
975
		{
976
			$current_action = 'moderate';
977
		}
978
		elseif ($context['current_action'] === 'moderate' && $context['allow_admin'])
979
		{
980
			$current_action = 'admin';
981
		}
982
983
		// Not all actions are simple.
984
		if (!empty($needs_action_hook))
985
		{
986
			call_integration_hook('integrate_current_action', array(&$current_action));
987
		}
988
989
		if (isset($context['menu_buttons'][$current_action]))
990
		{
991
			$context['menu_buttons'][$current_action]['active_button'] = true;
992
		}
993
	}
994
995
	/**
996
	 * Load the base JS that gives Elkarte a nice rack
997
	 */
998
	public function loadThemeJavascript()
999
	{
1000
		global $settings, $context, $modSettings, $scripturl, $txt, $options;
1001
1002
		// Queue our Javascript
1003
		loadJavascriptFile(array('elk_jquery_plugins.js', 'script.js', 'script_elk.js', 'theme.js'));
1004
1005
		// Default JS variables for use in every theme
1006
		$this->addJavascriptVar(array(
1007
				'elk_theme_url' => JavaScriptEscape($settings['theme_url']),
1008
				'elk_default_theme_url' => JavaScriptEscape($settings['default_theme_url']),
1009
				'elk_images_url' => JavaScriptEscape($settings['images_url']),
1010
				'elk_smiley_url' => JavaScriptEscape($modSettings['smileys_url']),
1011
				'elk_scripturl' => '\'' . $scripturl . '\'',
1012
				'elk_iso_case_folding' => detectServer()->is('iso_case_folding') ? 'true' : 'false',
1013
				'elk_charset' => '"UTF-8"',
1014
				'elk_session_id' => JavaScriptEscape($context['session_id']),
1015
				'elk_session_var' => JavaScriptEscape($context['session_var']),
1016
				'elk_member_id' => $context['user']['id'],
1017
				'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
1018
				'ajax_notification_cancel_text' => JavaScriptEscape($txt['modify_cancel']),
1019
				'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
1020
				'use_click_menu' => !empty($options['use_click_menu']) ? 'true' : 'false',
1021
				'todayMod' => !empty($modSettings['todayMod']) ? (int) $modSettings['todayMod'] : 0)
1022
		);
1023
1024
		// Auto video embedding enabled, then load the needed JS
1025
		$this->autoEmbedVideo();
1026
1027
		// Prettify code tags? Load the needed JS and CSS.
1028
		$this->addCodePrettify();
1029
1030
		// Relative times for posts?
1031
		$this->relativeTimes();
1032
1033
		// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
1034
		if ((!empty($modSettings['mail_next_send']) && $modSettings['mail_next_send'] < time() && empty($modSettings['mail_queue_use_cron'])) || empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
1035
		{
1036
			$this->doScheduledSendMail();
1037
		}
1038
	}
1039
1040
	/**
1041
	 * Makes the default layers and languages available
1042
	 *
1043
	 * - Loads index and addon language files as needed
1044
	 * - Loads xml, index or no templates as needed
1045
	 * - Loads templates as defined by $settings['theme_templates']
1046
	 */
1047
	public function loadDefaultLayers()
1048
	{
1049
		global $settings;
1050
1051
		$simpleActions = array(
1052
			'quickhelp',
1053
			'printpage',
1054
			'quotefast',
1055
			'spellcheck',
1056
		);
1057
1058
		call_integration_hook('integrate_simple_actions', array(&$simpleActions));
1059
1060
		// Output is fully XML
1061
		if (isset($_REQUEST['xml']))
1062
		{
1063
			loadLanguage('index+Addons');
1064
1065
			// @todo added because some $settings in template_init are necessary even in
1066
			// xml mode. Maybe move template_init to a settings file?
1067
			$this->templates->load('index');
1068
			$this->templates->load('Xml');
1069
			$this->layers->removeAll();
1070
		}
1071
		// These actions don't require the index template at all.
1072
		elseif (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], $simpleActions))
1073
		{
1074
			loadLanguage('index+Addons');
1075
			$this->layers->removeAll();
1076
		}
1077
		else
1078
		{
1079
			// Custom templates to load, or just default?
1080 View Code Duplication
			if (isset($settings['theme_templates']))
1081
			{
1082
				$templates = explode(',', $settings['theme_templates']);
1083
			}
1084
			else
1085
			{
1086
				$templates = array('index');
1087
			}
1088
1089
			// Load each template...
1090
			foreach ($templates as $template)
1091
				$this->templates->load($template);
1092
1093
			// ...and attempt to load their associated language files.
1094
			$required_files = implode('+', array_merge($templates, array('Addons')));
1095
			loadLanguage($required_files, '', false);
1096
1097
			// Custom template layers?
1098
			if (isset($settings['theme_layers']))
1099
			{
1100
				$layers = explode(',', $settings['theme_layers']);
1101
			}
1102
			else
1103
			{
1104
				$layers = array('html', 'body');
1105
			}
1106
1107
			$template_layers = \Template_Layers::instance(true);
1108
			foreach ($layers as $layer)
1109
			{
1110
				$template_layers->addBegin($layer);
1111
			}
1112
		}
1113
	}
1114
1115
	/**
1116
	 * If a variant CSS is needed, this loads it
1117
	 */
1118
	public function loadThemeVariant()
1119
	{
1120
		global $context, $settings, $options;
1121
1122
		// Overriding - for previews and that ilk.
1123
		if (!empty($_REQUEST['variant']))
1124
		{
1125
			$_SESSION['id_variant'] = $_REQUEST['variant'];
1126
		}
1127
1128
		// User selection?
1129
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
1130
		{
1131
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
1132
		}
1133
1134
		// If not a user variant, select the default.
1135
		if ($context['theme_variant'] === '' || !in_array($context['theme_variant'], $settings['theme_variants']))
1136
		{
1137
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
1138
		}
1139
1140
		// Do this to keep things easier in the templates.
1141
		$context['theme_variant'] = '_' . $context['theme_variant'];
1142
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
1143
1144
		// The most efficient way of writing multi themes is to use a master index.css plus variant.css files.
1145
		if (!empty($context['theme_variant']))
1146
		{
1147
			loadCSSFile($context['theme_variant'] . '/index' . $context['theme_variant'] . '.css');
1148
1149
			// Variant icon definitions?
1150 View Code Duplication
			if (file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/icons_svg' . $context['theme_variant'] . '.css'))
1151
			{
1152
				loadCSSFile($context['theme_variant'] .  '/icons_svg' . $context['theme_variant'] . '.css');
1153
			}
1154
1155
			// Load a theme variant custom CSS
1156 View Code Duplication
			if (!empty($context['theme_variant']) && file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/custom' . $context['theme_variant'] . '.css'))
1157
			{
1158
				loadCSSFile($context['theme_variant'] . '/custom' . $context['theme_variant'] . '.css');
1159
			}
1160
		}
1161
	}
1162
}
1163