Completed
Pull Request — master (#3325)
by Emanuele
11:19
created

Theme::template_inlinecss()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 13
dl 0
loc 28
rs 9.2222
c 0
b 0
f 0
nc 10
nop 0
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.6
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
		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
		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
			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
			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
		if (!empty($context['security_controls_files']))
442
		{
443
			$context['security_controls_files']['type'] = 'serious';
444
			template_show_error('security_controls_files');
445
		}
446
447
		if (!empty($context['security_controls_query']))
448
		{
449
			$context['security_controls_query']['type'] = 'serious';
450
			template_show_error('security_controls_query');
451
		}
452
453
		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
				embed_limit : ' . (!empty($modSettings['video_embed_limit']) ? $modSettings['video_embed_limit'] : 25) . ',
510
				preview_image : ' . JavaScriptEscape($txt['preview_image']) . ',
511
				ctp_video : ' . JavaScriptEscape($txt['ctp_video']) . ',
512
				hide_video : ' . JavaScriptEscape($txt['hide_video']) . ',
513
				youtube : ' . JavaScriptEscape($txt['youtube']) . ',
514
				vimeo : ' . JavaScriptEscape($txt['vimeo']) . ',
515
				dailymotion : ' . JavaScriptEscape($txt['dailymotion']) . '
516
			});', true);
517
518
			loadJavascriptFile('elk_jquery_embed.js', array('defer' => true));
519
		}
520
	}
521
522
	/**
523
	 * Ensures we kick the mail queue from time to time so that it gets
524
	 * checked as often as possible.
525
	 */
526
	public function doScheduledSendMail()
527
	{
528
		global $modSettings;
529
530
		if (isBrowser('possibly_robot'))
531
		{
532
			// @todo Maybe move this somewhere better?!
533
			$controller = new \ScheduledTasks_Controller();
534
535
			// What to do, what to do?!
536
			if (empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time())
537
			{
538
				$controller->action_autotask();
539
			}
540
			else
541
			{
542
				$controller->action_reducemailqueue();
543
			}
544
		}
545
		else
546
		{
547
			$type = empty($modSettings['next_task_time']) || $modSettings['next_task_time'] < time() ? 'task' : 'mailq';
548
			$ts = $type === 'mailq' ? $modSettings['mail_next_send'] : $modSettings['next_task_time'];
549
550
			addInlineJavascript('
551
		function elkAutoTask()
552
		{
553
			var tempImage = new Image();
554
			tempImage.src = elk_scripturl + "?scheduled=' . $type . ';ts=' . $ts . '";
555
		}
556
		window.setTimeout("elkAutoTask();", 1);', true);
557
		}
558
	}
559
560
	/**
561
	 * Relative times require a few variables be set in the JS
562
	 */
563
	public function relativeTimes()
564
	{
565
		global $modSettings, $context, $txt;
566
567
		// Relative times?
568
		if (!empty($modSettings['todayMod']) && $modSettings['todayMod'] > 2)
569
		{
570
			addInlineJavascript('
571
			var oRttime = ({
572
				referenceTime : ' . forum_time() * 1000 . ',
573
				now : ' . JavaScriptEscape($txt['rt_now']) . ',
574
				minute : ' . JavaScriptEscape($txt['rt_minute']) . ',
575
				minutes : ' . JavaScriptEscape($txt['rt_minutes']) . ',
576
				hour : ' . JavaScriptEscape($txt['rt_hour']) . ',
577
				hours : ' . JavaScriptEscape($txt['rt_hours']) . ',
578
				day : ' . JavaScriptEscape($txt['rt_day']) . ',
579
				days : ' . JavaScriptEscape($txt['rt_days']) . ',
580
				week : ' . JavaScriptEscape($txt['rt_week']) . ',
581
				weeks : ' . JavaScriptEscape($txt['rt_weeks']) . ',
582
				month : ' . JavaScriptEscape($txt['rt_month']) . ',
583
				months : ' . JavaScriptEscape($txt['rt_months']) . ',
584
				year : ' . JavaScriptEscape($txt['rt_year']) . ',
585
				years : ' . JavaScriptEscape($txt['rt_years']) . ',
586
			});
587
			updateRelativeTime();', true);
588
589
			$context['using_relative_time'] = true;
590
		}
591
	}
592
593
	/**
594
	 * Sets up the basic theme context stuff.
595
	 *
596
	 * @param bool $forceload = false
597
	 */
598
	public function setupThemeContext($forceload = false)
599
	{
600
		global $modSettings, $user_info, $scripturl, $context, $settings, $options, $txt;
601
602
		static $loaded = false;
603
604
		// Under SSI this function can be called more then once.  That can cause some problems.
605
		// So only run the function once unless we are forced to run it again.
606
		if ($loaded && !$forceload)
607
		{
608
			return;
609
		}
610
611
		$loaded = true;
612
613
		$context['current_time'] = standardTime(time(), false);
614
		$context['current_action'] = isset($_GET['action']) ? $_GET['action'] : '';
615
		$context['show_quick_login'] = !empty($modSettings['enableVBStyleLogin']) && $user_info['is_guest'];
616
		$context['robot_no_index'] = in_array($context['current_action'], $this->no_index_actions);
617
618
		$bbc_parser = \BBC\ParserWrapper::instance();
619
620
		// Get some news...
621
		$context['news_lines'] = array_filter(explode("\n", str_replace("\r", '', trim(addslashes($modSettings['news'])))));
622
		for ($i = 0, $n = count($context['news_lines']); $i < $n; $i++)
623
		{
624
			if (trim($context['news_lines'][$i]) === '')
625
			{
626
				continue;
627
			}
628
629
			// Clean it up for presentation ;).
630
			$context['news_lines'][$i] = $bbc_parser->parseNews(stripslashes(trim($context['news_lines'][$i])));
631
		}
632
633
		// If we have some, setup for display
634
		if (!empty($context['news_lines']))
635
		{
636
			$context['random_news_line'] = $context['news_lines'][mt_rand(0, count($context['news_lines']) - 1)];
637
			$context['upper_content_callbacks'][] = 'news_fader';
638
		}
639
640
		if (!$user_info['is_guest'])
641
		{
642
			$context['user']['messages'] = &$user_info['messages'];
643
			$context['user']['unread_messages'] = &$user_info['unread_messages'];
644
			$context['user']['mentions'] = &$user_info['mentions'];
645
646
			// Personal message popup...
647
			if ($user_info['unread_messages'] > (isset($_SESSION['unread_messages']) ? $_SESSION['unread_messages'] : 0))
648
			{
649
				$context['user']['popup_messages'] = true;
650
			}
651
			else
652
			{
653
				$context['user']['popup_messages'] = false;
654
			}
655
656
			$_SESSION['unread_messages'] = $user_info['unread_messages'];
657
658
			$context['user']['avatar'] = array(
659
				'href' => !empty($user_info['avatar']['href']) ? $user_info['avatar']['href'] : '',
660
				'image' => !empty($user_info['avatar']['image']) ? $user_info['avatar']['image'] : '',
661
			);
662
663
			// Figure out how long they've been logged in.
664
			$context['user']['total_time_logged_in'] = array(
665
				'days' => floor($user_info['total_time_logged_in'] / 86400),
666
				'hours' => floor(($user_info['total_time_logged_in'] % 86400) / 3600),
667
				'minutes' => floor(($user_info['total_time_logged_in'] % 3600) / 60)
668
			);
669
		}
670
		else
671
		{
672
			$context['user']['messages'] = 0;
673
			$context['user']['unread_messages'] = 0;
674
			$context['user']['mentions'] = 0;
675
			$context['user']['avatar'] = array();
676
			$context['user']['total_time_logged_in'] = array('days' => 0, 'hours' => 0, 'minutes' => 0);
677
			$context['user']['popup_messages'] = false;
678
679
			if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1)
680
			{
681
				$txt['welcome_guest'] .= $txt['welcome_guest_activate'];
682
			}
683
684
			$txt['welcome_guest'] = replaceBasicActionUrl($txt['welcome_guest']);
685
686
			// If we've upgraded recently, go easy on the passwords.
687
			if (!empty($modSettings['enable_password_conversion']))
688
			{
689
				$context['disable_login_hashing'] = true;
690
			}
691
		}
692
693
		// Setup the main menu items.
694
		$this->setupMenuContext();
695
696
		if (empty($settings['theme_version']))
697
		{
698
			$context['show_vBlogin'] = $context['show_quick_login'];
699
		}
700
701
		// This is here because old index templates might still use it.
702
		$context['show_news'] = !empty($settings['enable_news']);
703
704
		$context['additional_dropdown_search'] = prepareSearchEngines();
705
706
		// This is done to allow theme authors to customize it as they want.
707
		$context['show_pm_popup'] = $context['user']['popup_messages'] && !empty($options['popup_messages']) && (!isset($_REQUEST['action']) || $_REQUEST['action'] !== 'pm');
708
709
		// Add the PM popup here instead. Theme authors can still override it simply by editing/removing the 'fPmPopup' in the array.
710
		if ($context['show_pm_popup'])
711
		{
712
			addInlineJavascript('
713
			$(function() {
714
				new smc_Popup({
715
					heading: ' . JavaScriptEscape($txt['show_personal_messages_heading']) . ',
716
					content: ' . JavaScriptEscape(sprintf($txt['show_personal_messages'], $context['user']['unread_messages'], $scripturl . '?action=pm')) . ',
717
					icon: \'i-envelope\'
718
				});
719
			});', true);
720
		}
721
722
		// This looks weird, but it's because BoardIndex.controller.php references the variable.
723
		$context['common_stats']['latest_member'] = array(
724
			'id' => $modSettings['latestMember'],
725
			'name' => $modSettings['latestRealName'],
726
			'href' => $scripturl . '?action=profile;u=' . $modSettings['latestMember'],
727
			'link' => '<a href="' . $scripturl . '?action=profile;u=' . $modSettings['latestMember'] . '">' . $modSettings['latestRealName'] . '</a>',
728
		);
729
730
		$context['common_stats'] = array(
731
			'total_posts' => comma_format($modSettings['totalMessages']),
732
			'total_topics' => comma_format($modSettings['totalTopics']),
733
			'total_members' => comma_format($modSettings['totalMembers']),
734
			'latest_member' => $context['common_stats']['latest_member'],
735
		);
736
737
		$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']);
738
739
		if (empty($settings['theme_version']))
740
		{
741
			addJavascriptVar(array('elk_scripturl' => '\'' . $scripturl . '\''));
742
		}
743
		addJavascriptVar(array('elk_forum_action' => '\'' . substr($modSettings['default_forum_action'], 1, -1) . '\''));
744
745
		if (!isset($context['page_title']))
746
		{
747
			$context['page_title'] = '';
748
		}
749
750
		// Set some specific vars.
751
		$context['page_title_html_safe'] = \Util::htmlspecialchars(un_htmlspecialchars($context['page_title'])) . (!empty($context['current_page']) ? ' - ' . $txt['page'] . ' ' . ($context['current_page'] + 1) : '');
752
753
		// 1.0 backward compatibility: if you already put the icon in the theme dir
754
		// use that one, otherwise the default
755
		// @deprecated since 1.1
756
		if (file_exists(BOARDDIR . '/mobile.png'))
757
		{
758
			$context['favicon'] = $scripturl . '/mobile.png';
759
		}
760
		else
761
		{
762
			$context['favicon'] = $settings['images_url'] . '/mobile.png';
763
		}
764
765
		// Since it's nice to have avatars all of the same size, and in some cases the size detection may fail,
766
		// let's add the css in any case
767
		if (!isset($context['html_headers']))
768
			$context['html_headers'] = '';
769
770
		if (!empty($modSettings['avatar_max_width']) || !empty($modSettings['avatar_max_height']))
771
		{
772
			$this->addCSSRules('
773
		.avatarresize {' . (!empty($modSettings['avatar_max_width']) ? '
774
			max-width:' . $modSettings['avatar_max_width'] . 'px;' : '') . (!empty($modSettings['avatar_max_height']) ? '
775
			max-height:' . $modSettings['avatar_max_height'] . 'px;' : '') . '
776
		}');
777
		}
778
779
		// Save some database hits, if a width for multiple wrappers is set in admin.
780
		if (!empty($settings['forum_width']))
781
		{
782
			$this->addCSSRules('
783
		.wrapper {width: ' . $settings['forum_width'] . ';}');
784
		}
785
	}
786
787
	/**
788
	 * Adds required support CSS files.
789
	 */
790
	public function loadSupportCSS()
791
	{
792
		global $modSettings, $settings;
793
794
		// Load the SVG support file with fallback to default theme
795
		loadCSSFile('icons_svg.css');
796
797
		// Load a base theme custom CSS file?
798
		if (file_exists($settings['theme_dir'] . '/css/custom.css'))
799
		{
800
			loadCSSFile('custom.css', array('fallback' => false));
801
		}
802
803
		// Load font Awesome fonts, @deprecated in 1.1 and will be removed in 2.0
804
		if (!empty($settings['require_font-awesome']) || !empty($modSettings['require_font-awesome']))
805
		{
806
			loadCSSFile('font-awesome.min.css');
807
		}
808
	}
809
810
	/**
811
	 * Sets up all of the top menu buttons
812
	 *
813
	 * What it does:
814
	 *
815
	 * - Defines every master item in the menu, as well as any sub-items
816
	 * - Ensures the chosen action is set so the menu is highlighted
817
	 * - Saves them in the cache if it is available and on
818
	 * - Places the results in $context
819
	 */
820
	public function setupMenuContext()
821
	{
822
		global $context, $modSettings, $user_info, $settings;
823
824
		// Set up the menu privileges.
825
		$context['allow_search'] = !empty($modSettings['allow_guestAccess']) ? allowedTo('search_posts') : (!$user_info['is_guest'] && allowedTo('search_posts'));
826
		$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'));
827
		$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'));
828
		$context['allow_memberlist'] = allowedTo('view_mlist');
829
		$context['allow_calendar'] = allowedTo('calendar_view') && !empty($modSettings['cal_enabled']);
830
		$context['allow_moderation_center'] = $context['user']['can_mod'];
831
		$context['allow_pm'] = allowedTo('pm_read');
832
833
		call_integration_hook('integrate_setup_allow');
834
835
		if ($context['allow_search'])
836
		{
837
			$context['theme_header_callbacks'] = elk_array_insert($context['theme_header_callbacks'], 'login_bar', array('search_bar'), 'after');
838
		}
839
840
		$cacheTime = $modSettings['lastActive'] * 60;
841
842
		// Update the Moderation menu items with action item totals
843
		if ($context['allow_moderation_center'])
844
		{
845
			// Get the numbers for the menu ...
846
			require_once(SUBSDIR . '/Moderation.subs.php');
847
			$menu_count = loadModeratorMenuCounts();
848
		}
849
850
		$menu_count['unread_messages'] = $context['user']['unread_messages'];
851
		$menu_count['mentions'] = $context['user']['mentions'];
852
853
		if (!empty($user_info['avatar']['href']))
854
		{
855
			$this->addCSSRules('
856
	.i-account:before {
857
		content: "";
858
		background-image: url("' . $user_info['avatar']['href'] . '");
859
	}');
860
		}
861
862
		// All the buttons we can possible want and then some, try pulling the final list of buttons from cache first.
863
		if (($menu_buttons = cache_get_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $cacheTime)) === null || time() - $cacheTime <= $modSettings['settings_updated'])
864
		{
865
			// Start things up: this is what we know by default
866
			require_once(SUBSDIR . '/Menu.subs.php');
867
			$buttons = loadDefaultMenuButtons();
868
869
			// Allow editing menu buttons easily.
870
			call_integration_hook('integrate_menu_buttons', array(&$buttons, &$menu_count));
871
872
			// Now we put the buttons in the context so the theme can use them.
873
			$menu_buttons = array();
874
			foreach ($buttons as $act => $button)
875
			{
876
				if (!empty($button['show']))
877
				{
878
					$button['active_button'] = false;
879
880
					// This button needs some action.
881
					if (isset($button['action_hook']))
882
					{
883
						$needs_action_hook = true;
884
					}
885
886
					if (isset($button['counter']) && !empty($menu_count[$button['counter']]))
887
					{
888
						$button['alttitle'] = $button['title'] . ' [' . $menu_count[$button['counter']] . ']';
889
						if (!empty($settings['menu_numeric_notice'][0]))
890
						{
891
							$button['title'] .= sprintf($settings['menu_numeric_notice'][0], $menu_count[$button['counter']]);
892
							$button['indicator'] = true;
893
						}
894
					}
895
896
					// Go through the sub buttons if there are any.
897
					if (isset($button['sub_buttons']))
898
					{
899
						foreach ($button['sub_buttons'] as $key => $subbutton)
900
						{
901
							if (empty($subbutton['show']))
902
							{
903
								unset($button['sub_buttons'][$key]);
904
							}
905
							elseif (isset($subbutton['counter']) && !empty($menu_count[$subbutton['counter']]))
906
							{
907
								$button['sub_buttons'][$key]['alttitle'] = $subbutton['title'] . ' [' . $menu_count[$subbutton['counter']] . ']';
908
								if (!empty($settings['menu_numeric_notice'][1]))
909
								{
910
									$button['sub_buttons'][$key]['title'] .= sprintf($settings['menu_numeric_notice'][1], $menu_count[$subbutton['counter']]);
911
								}
912
913
								// 2nd level sub buttons next...
914
								if (isset($subbutton['sub_buttons']))
915
								{
916
									foreach ($subbutton['sub_buttons'] as $key2 => $subbutton2)
917
									{
918
										$button['sub_buttons'][$key]['sub_buttons'][$key2] = $subbutton2;
919
										if (empty($subbutton2['show']))
920
										{
921
											unset($button['sub_buttons'][$key]['sub_buttons'][$key2]);
922
										}
923
										elseif (isset($subbutton2['counter']) && !empty($menu_count[$subbutton2['counter']]))
924
										{
925
											$button['sub_buttons'][$key]['sub_buttons'][$key2]['alttitle'] = $subbutton2['title'] . ' [' . $menu_count[$subbutton2['counter']] . ']';
926
											if (!empty($settings['menu_numeric_notice'][2]))
927
											{
928
												$button['sub_buttons'][$key]['sub_buttons'][$key2]['title'] .= sprintf($settings['menu_numeric_notice'][2], $menu_count[$subbutton2['counter']]);
929
											}
930
											unset($menu_count[$subbutton2['counter']]);
931
										}
932
									}
933
								}
934
							}
935
						}
936
					}
937
938
					$menu_buttons[$act] = $button;
939
				}
940
			}
941
942
			if (!empty($modSettings['cache_enable']) && $modSettings['cache_enable'] >= 2)
943
			{
944
				cache_put_data('menu_buttons-' . implode('_', $user_info['groups']) . '-' . $user_info['language'], $menu_buttons, $cacheTime);
945
			}
946
		}
947
948
		if (!empty($menu_buttons['profile']['sub_buttons']['logout']))
949
		{
950
			$menu_buttons['profile']['sub_buttons']['logout']['href'] .= ';' . $context['session_var'] . '=' . $context['session_id'];
951
		}
952
953
		$context['menu_buttons'] = $menu_buttons;
954
955
		// Figure out which action we are doing so we can set the active tab.
956
		// Default to home.
957
		$current_action = 'home';
958
959
		if (isset($context['menu_buttons'][$context['current_action']]))
960
		{
961
			$current_action = $context['current_action'];
962
		}
963
		elseif ($context['current_action'] === 'profile')
964
		{
965
			$current_action = 'pm';
966
		}
967
		elseif ($context['current_action'] === 'theme')
968
		{
969
			$current_action = isset($_REQUEST['sa']) && $_REQUEST['sa'] === 'pick' ? 'profile' : 'admin';
970
		}
971
		elseif ($context['current_action'] === 'login2' || ($user_info['is_guest'] && $context['current_action'] === 'reminder'))
972
		{
973
			$current_action = 'login';
974
		}
975
		elseif ($context['current_action'] === 'groups' && $context['allow_moderation_center'])
976
		{
977
			$current_action = 'moderate';
978
		}
979
		elseif ($context['current_action'] === 'moderate' && $context['allow_admin'])
980
		{
981
			$current_action = 'admin';
982
		}
983
984
		// Not all actions are simple.
985
		if (!empty($needs_action_hook))
986
		{
987
			call_integration_hook('integrate_current_action', array(&$current_action));
988
		}
989
990
		if (isset($context['menu_buttons'][$current_action]))
991
		{
992
			$context['menu_buttons'][$current_action]['active_button'] = true;
993
		}
994
	}
995
996
	/**
997
	 * Load the base JS that gives Elkarte a nice rack
998
	 */
999
	public function loadThemeJavascript()
1000
	{
1001
		global $settings, $context, $modSettings, $scripturl, $txt, $options;
1002
1003
		// Queue our Javascript
1004
		loadJavascriptFile(array('elk_jquery_plugins.js', 'script.js', 'script_elk.js', 'theme.js'));
1005
1006
		// Default JS variables for use in every theme
1007
		$this->addJavascriptVar(array(
1008
				'elk_theme_url' => JavaScriptEscape($settings['theme_url']),
1009
				'elk_default_theme_url' => JavaScriptEscape($settings['default_theme_url']),
1010
				'elk_images_url' => JavaScriptEscape($settings['images_url']),
1011
				'elk_smiley_url' => JavaScriptEscape($modSettings['smileys_url']),
1012
				'elk_scripturl' => '\'' . $scripturl . '\'',
1013
				'elk_iso_case_folding' => detectServer()->is('iso_case_folding') ? 'true' : 'false',
1014
				'elk_charset' => '"UTF-8"',
1015
				'elk_session_id' => JavaScriptEscape($context['session_id']),
1016
				'elk_session_var' => JavaScriptEscape($context['session_var']),
1017
				'elk_member_id' => $context['user']['id'],
1018
				'ajax_notification_text' => JavaScriptEscape($txt['ajax_in_progress']),
1019
				'ajax_notification_cancel_text' => JavaScriptEscape($txt['modify_cancel']),
1020
				'help_popup_heading_text' => JavaScriptEscape($txt['help_popup']),
1021
				'use_click_menu' => !empty($options['use_click_menu']) ? 'true' : 'false',
1022
				'todayMod' => !empty($modSettings['todayMod']) ? (int) $modSettings['todayMod'] : 0)
1023
		);
1024
1025
		// Auto video embedding enabled, then load the needed JS
1026
		$this->autoEmbedVideo();
1027
1028
		// Prettify code tags? Load the needed JS and CSS.
1029
		$this->addCodePrettify();
1030
1031
		// Relative times for posts?
1032
		$this->relativeTimes();
1033
1034
		// If we think we have mail to send, let's offer up some possibilities... robots get pain (Now with scheduled task support!)
1035
		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())
1036
		{
1037
			$this->doScheduledSendMail();
1038
		}
1039
	}
1040
1041
	/**
1042
	 * Makes the default layers and languages available
1043
	 *
1044
	 * - Loads index and addon language files as needed
1045
	 * - Loads xml, index or no templates as needed
1046
	 * - Loads templates as defined by $settings['theme_templates']
1047
	 */
1048
	public function loadDefaultLayers()
1049
	{
1050
		global $settings;
1051
1052
		$simpleActions = array(
1053
			'quickhelp',
1054
			'printpage',
1055
			'quotefast',
1056
			'spellcheck',
1057
		);
1058
1059
		call_integration_hook('integrate_simple_actions', array(&$simpleActions));
1060
1061
		// Output is fully XML
1062
		if (isset($_REQUEST['xml']))
1063
		{
1064
			loadLanguage('index+Addons');
1065
1066
			// @todo added because some $settings in template_init are necessary even in
1067
			// xml mode. Maybe move template_init to a settings file?
1068
			$this->templates->load('index');
1069
			$this->templates->load('Xml');
1070
			$this->layers->removeAll();
1071
		}
1072
		// These actions don't require the index template at all.
1073
		elseif (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], $simpleActions))
1074
		{
1075
			loadLanguage('index+Addons');
1076
			$this->layers->removeAll();
1077
		}
1078
		else
1079
		{
1080
			// Custom templates to load, or just default?
1081
			if (isset($settings['theme_templates']))
1082
			{
1083
				$templates = explode(',', $settings['theme_templates']);
1084
			}
1085
			else
1086
			{
1087
				$templates = array('index');
1088
			}
1089
1090
			// Load each template...
1091
			foreach ($templates as $template)
1092
				$this->templates->load($template);
1093
1094
			// ...and attempt to load their associated language files.
1095
			$required_files = implode('+', array_merge($templates, array('Addons')));
1096
			loadLanguage($required_files, '', false);
1097
1098
			// Custom template layers?
1099
			if (isset($settings['theme_layers']))
1100
			{
1101
				$layers = explode(',', $settings['theme_layers']);
1102
			}
1103
			else
1104
			{
1105
				$layers = array('html', 'body');
1106
			}
1107
1108
			$template_layers = \Template_Layers::instance(true);
1109
			foreach ($layers as $layer)
1110
			{
1111
				$template_layers->addBegin($layer);
1112
			}
1113
		}
1114
	}
1115
1116
	/**
1117
	 * If a variant CSS is needed, this loads it
1118
	 */
1119
	public function loadThemeVariant()
1120
	{
1121
		global $context, $settings, $options;
1122
1123
		// Overriding - for previews and that ilk.
1124
		if (!empty($_REQUEST['variant']))
1125
		{
1126
			$_SESSION['id_variant'] = $_REQUEST['variant'];
1127
		}
1128
1129
		// User selection?
1130
		if (empty($settings['disable_user_variant']) || allowedTo('admin_forum'))
1131
		{
1132
			$context['theme_variant'] = !empty($_SESSION['id_variant']) ? $_SESSION['id_variant'] : (!empty($options['theme_variant']) ? $options['theme_variant'] : '');
1133
		}
1134
1135
		// If not a user variant, select the default.
1136
		if ($context['theme_variant'] === '' || !in_array($context['theme_variant'], $settings['theme_variants']))
1137
		{
1138
			$context['theme_variant'] = !empty($settings['default_variant']) && in_array($settings['default_variant'], $settings['theme_variants']) ? $settings['default_variant'] : $settings['theme_variants'][0];
1139
		}
1140
1141
		// Do this to keep things easier in the templates.
1142
		$context['theme_variant'] = '_' . $context['theme_variant'];
1143
		$context['theme_variant_url'] = $context['theme_variant'] . '/';
1144
1145
		// The most efficient way of writing multi themes is to use a master index.css plus variant.css files.
1146
		if (!empty($context['theme_variant']))
1147
		{
1148
			loadCSSFile($context['theme_variant'] . '/index' . $context['theme_variant'] . '.css');
1149
1150
			// Variant icon definitions?
1151
			if (file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/icons_svg' . $context['theme_variant'] . '.css'))
1152
			{
1153
				loadCSSFile($context['theme_variant'] .  '/icons_svg' . $context['theme_variant'] . '.css');
1154
			}
1155
1156
			// Load a theme variant custom CSS
1157
			if (!empty($context['theme_variant']) && file_exists($settings['theme_dir'] . '/css/' . $context['theme_variant'] . '/custom' . $context['theme_variant'] . '.css'))
1158
			{
1159
				loadCSSFile($context['theme_variant'] . '/custom' . $context['theme_variant'] . '.css');
1160
			}
1161
		}
1162
	}
1163
}
1164