1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @package ElkArte Forum |
5
|
|
|
* @copyright ElkArte Forum contributors |
6
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
7
|
|
|
* |
8
|
|
|
* This file contains code covered by: |
9
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
10
|
|
|
* |
11
|
|
|
* @version 2.0 dev |
12
|
|
|
* |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* This template is, perhaps, the most important template in the theme. It |
17
|
|
|
* contains the main template layer that displays the header and footer of |
18
|
|
|
* the forum, namely with body_above and body_below. It also contains the |
19
|
|
|
* menu sub template, which appropriately displays the menu; the init sub |
20
|
|
|
* template, which is there to set the theme up; (init can be missing.) and |
21
|
|
|
* the breadcrumb sub template, which sorts out the breadcrumbs. |
22
|
|
|
* |
23
|
|
|
* The init sub template should load any data and set any hardcoded options. |
24
|
|
|
* |
25
|
|
|
* The body_above sub template is what is shown above the main content, and |
26
|
|
|
* should contain anything that should be shown up there. |
27
|
|
|
* |
28
|
|
|
* The body_below sub template, conversely, is shown after the main content. |
29
|
|
|
* It should probably contain the copyright statement and some other things. |
30
|
|
|
* |
31
|
|
|
* The breadcrumb sub template should display the breadcrumbs, using the data |
32
|
|
|
* in the $context['breadcrumbs'] variable. |
33
|
|
|
* |
34
|
|
|
* The menu sub template should display all the relevant buttons the user |
35
|
|
|
* wants and or needs. |
36
|
|
|
*/ |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Start off the template by loading some helpers like |
40
|
|
|
* quick buttons, page index, etc |
41
|
|
|
*/ |
42
|
|
|
function template_Index_init() |
43
|
|
|
{ |
44
|
|
|
theme()->getTemplates()->load('GenericHelpers'); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Simplify the use of callbacks in the templates. |
49
|
|
|
* |
50
|
|
|
* @param string $id - A prefix for the template functions the final name |
51
|
|
|
* should look like: template_{$id}_{$array[n]} |
52
|
|
|
* @param string[] $array - The array of function suffixes |
53
|
|
|
*/ |
54
|
|
|
function call_template_callbacks($id, $array) |
55
|
|
|
{ |
56
|
|
|
if (empty($array)) |
57
|
|
|
{ |
58
|
|
|
return; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
foreach ($array as $callback) |
62
|
|
|
{ |
63
|
|
|
$func = 'template_' . $id . '_' . $callback; |
64
|
|
|
if (function_exists($func)) |
65
|
|
|
{ |
66
|
|
|
$func(); |
67
|
|
|
} |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* The main sub template above the content. |
73
|
|
|
*/ |
74
|
|
|
function template_html_above() |
75
|
|
|
{ |
76
|
|
|
global $context, $scripturl, $txt; |
77
|
|
|
|
78
|
|
|
// Show right to left and the character set for ease of translating. |
79
|
|
|
echo '<!DOCTYPE html> |
80
|
|
|
<html dir=', $context['right_to_left'] ? ' "RTL"' : 'LTR', ' lang="', str_replace('_', '-', $txt['lang_locale']), '"> |
81
|
|
|
<head> |
82
|
|
|
<title>', $context['page_title_html_safe'], '</title> |
83
|
|
|
<meta charset="utf-8" />'; |
84
|
|
|
|
85
|
|
|
$description = $context['page_title_html_safe']; |
86
|
|
|
if (isset($context['page_description'])) |
87
|
|
|
{ |
88
|
|
|
$description .= ': ' . $context['page_description']; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
echo ' |
92
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> |
93
|
|
|
<meta name="mobile-web-app-capable" content="yes" /> |
94
|
|
|
<meta name="description" content="', $description, '" /> |
95
|
|
|
<meta name="theme-color" content="', $context['theme-color'], '" />'; |
96
|
|
|
|
97
|
|
|
// Please don't index these Mr Robot. |
98
|
|
|
if (!empty($context['robot_no_index'])) |
99
|
|
|
{ |
100
|
|
|
echo ' |
101
|
|
|
<meta name="robots" content="noindex" />'; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// If we have any Open Graph data, here is where is inserted. |
105
|
|
|
if (!empty($context['open_graph'])) |
106
|
|
|
{ |
107
|
|
|
echo ' |
108
|
|
|
' .implode("\n\t", $context['open_graph']); |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// Present a canonical url for search engines to prevent duplicate content in their indices. |
112
|
|
|
if (!empty($context['canonical_url'])) |
113
|
|
|
{ |
114
|
|
|
echo ' |
115
|
|
|
<link rel="canonical" href="', $context['canonical_url'], '" />'; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
// Various icons and optionally a PWA manifest |
119
|
|
|
echo ' |
120
|
|
|
<link rel="icon" sizes="any" href="' . $context['favicon'] . '" /> |
121
|
|
|
<link rel="apple-touch-icon" href="' . $context['apple_touch'] . '" />'; |
122
|
|
|
|
123
|
|
|
if (!empty($context['pwa_manifest_enabled'])) |
124
|
|
|
{ |
125
|
|
|
echo ' |
126
|
|
|
<link rel="manifest" href="./elkManifest.php">'; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
// Show all the relative links, such as help, search, contents, and the like. |
130
|
|
|
echo ' |
131
|
|
|
<link rel="help" href="', getUrl('action', ['action' => 'help']), '" /> |
132
|
|
|
<link rel="contents" href="', $scripturl, '" />', ($context['allow_search'] ? ' |
133
|
|
|
<link rel="search" href="' . getUrl('action', ['action' => 'search']) . '" />' : ''); |
134
|
|
|
|
135
|
|
|
// If RSS feeds are enabled, advertise the presence of one. |
136
|
|
|
if (!empty($context['newsfeed_urls'])) |
137
|
|
|
{ |
138
|
|
|
echo ' |
139
|
|
|
<link rel="alternate" type="application/rss+xml" title="', $context['forum_name_html_safe'], ' - ', $txt['rss'], '" href="', $context['newsfeed_urls']['rss'], '" /> |
140
|
|
|
<link rel="alternate" type="application/rss+xml" title="', $context['forum_name_html_safe'], ' - ', $txt['atom'], '" href="', $context['newsfeed_urls']['atom'], '" />'; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// If we're viewing a topic, these should be the previous and next topics, respectively. |
144
|
|
|
if (!empty($context['links']['next'])) |
145
|
|
|
{ |
146
|
|
|
echo ' |
147
|
|
|
<link rel="next" href="', $context['links']['next'], '" />'; |
148
|
|
|
} |
149
|
|
|
elseif (!empty($context['current_topic'])) |
150
|
|
|
{ |
151
|
|
|
echo ' |
152
|
|
|
<link rel="next" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=next" />'; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
if (!empty($context['links']['prev'])) |
156
|
|
|
{ |
157
|
|
|
echo ' |
158
|
|
|
<link rel="prev" href="', $context['links']['prev'], '" />'; |
159
|
|
|
} |
160
|
|
|
elseif (!empty($context['current_topic'])) |
161
|
|
|
{ |
162
|
|
|
echo ' |
163
|
|
|
<link rel="prev" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=prev" />'; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// If we're in a board, or a topic for that matter, the index will be the board's index. |
167
|
|
|
if (!empty($context['current_board'])) |
168
|
|
|
{ |
169
|
|
|
echo ' |
170
|
|
|
<link rel="index" href="', $scripturl, '?board=', $context['current_board'], '.0" />'; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// load in css from addons or themes, do it first so overrides are possible |
174
|
|
|
theme()->themeCss()->template_css(); |
175
|
|
|
|
176
|
|
|
// load in any javascript files and inline from addons and themes |
177
|
|
|
theme()->themeJs()->template_javascript(); |
178
|
|
|
|
179
|
|
|
// load in any inline css files from addons and themes |
180
|
|
|
theme()->themeCss()->template_inlinecss(); |
181
|
|
|
|
182
|
|
|
// Output any remaining HTML headers. (from addons, maybe?) |
183
|
|
|
echo $context['html_headers']; |
184
|
|
|
|
185
|
|
|
echo ' |
186
|
|
|
</head>'; |
187
|
|
|
|
188
|
|
|
// Start defining the body class |
189
|
|
|
$bodyClass = 'action_'; |
190
|
|
|
|
191
|
|
|
if (!empty($context['current_action'])) |
192
|
|
|
{ |
193
|
|
|
$bodyClass .= htmlspecialchars($context['current_action'], ENT_COMPAT, 'UTF-8'); |
194
|
|
|
} |
195
|
|
|
elseif (!empty($context['current_board'])) |
196
|
|
|
{ |
197
|
|
|
$bodyClass .= 'messageindex'; |
198
|
|
|
} |
199
|
|
|
elseif (!empty($context['current_topic'])) |
200
|
|
|
{ |
201
|
|
|
$bodyClass .= 'display'; |
202
|
|
|
} |
203
|
|
|
else |
204
|
|
|
{ |
205
|
|
|
$bodyClass .= 'home'; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
if (!empty($context['current_board'])) |
209
|
|
|
{ |
210
|
|
|
$bodyClass .= ' board_' . htmlspecialchars($context['current_board'], ENT_COMPAT, 'UTF-8'); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
echo ' |
214
|
|
|
<body class="', $bodyClass . '">'; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Section above the main contents of the page, after opening the body tag |
219
|
|
|
*/ |
220
|
|
|
function template_body_above() |
221
|
|
|
{ |
222
|
|
|
global $context, $settings, $txt; |
223
|
|
|
|
224
|
|
|
// Go to top/bottom of page links and skipnav link for a11y. |
225
|
|
|
echo ' |
226
|
|
|
<a id="top" href="#skipnav" tabindex="0">', $txt['skip_nav'], '</a> |
227
|
|
|
<a id="gotop" href="#top_section" title="', $txt['go_up'], '">↑</a> |
228
|
|
|
<a id="gobottom" href="#footer_section" title="', $txt['go_down'], '">↓</a>'; |
229
|
|
|
|
230
|
|
|
echo ' |
231
|
|
|
<header id="top_section" class="', (empty($context['minmax_preferences']['upshrink']) ? 'th_expand' : 'th_collapse'), '"> |
232
|
|
|
<aside id="top_header" class="wrapper">'; |
233
|
|
|
|
234
|
|
|
// Load in all register header templates |
235
|
|
|
call_template_callbacks('th', $context['theme_header_callbacks']); |
236
|
|
|
|
237
|
|
|
echo ' |
238
|
|
|
</aside> |
239
|
|
|
<section id="header" class="wrapper', empty($settings['header_layout']) ? '' : ($settings['header_layout'] == 1 ? ' centerheader' : ' rightheader'), empty($context['minmax_preferences']['upshrink']) ? '"' : ' hide" aria-hidden="true"', '> |
240
|
|
|
<h1 id="forumtitle"> |
241
|
|
|
<a class="forumlink" href="', getUrl('boardindex', []), '">', $context['forum_name'], '</a>'; |
242
|
|
|
|
243
|
|
|
echo ' |
244
|
|
|
<span id="logobox"> |
245
|
|
|
<img id="logo" src="', $context['header_logo_url_html_safe'], '" alt="', $context['forum_name_html_safe'], '" title="', $context['forum_name_html_safe'], '" />', empty($settings['site_slogan']) ? '' : ' |
246
|
|
|
<span id="siteslogan">' . $settings['site_slogan'] . '</span>', ' |
247
|
|
|
</span> |
248
|
|
|
</h1>'; |
249
|
|
|
|
250
|
|
|
// Show the menu here, according to the menu sub template. |
251
|
|
|
echo ' |
252
|
|
|
</section>'; |
253
|
|
|
|
254
|
|
|
template_menu(); |
255
|
|
|
|
256
|
|
|
echo ' |
257
|
|
|
</header> |
258
|
|
|
<div id="wrapper" class="wrapper"> |
259
|
|
|
<aside id="upper_section"', empty($context['minmax_preferences']['upshrink']) ? '' : ' class="hide" aria-hidden="true"', '>'; |
260
|
|
|
|
261
|
|
|
// Load in all registered upper content templates |
262
|
|
|
call_template_callbacks('uc', $context['upper_content_callbacks']); |
263
|
|
|
|
264
|
|
|
echo ' |
265
|
|
|
</aside>'; |
266
|
|
|
|
267
|
|
|
// Show the navigation tree. |
268
|
|
|
theme_breadcrumbs(); |
269
|
|
|
|
270
|
|
|
// The main content should go here. |
271
|
|
|
echo ' |
272
|
|
|
<div id="main_content_section"> |
273
|
|
|
<a id="skipnav"></a>'; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* More or less a place holder for now, sits at the very page top. |
278
|
|
|
* The maintenance mode warning for admins is an obvious one, but this could also be used for moderation notifications. |
279
|
|
|
* I also assumed this would be an obvious place for sites to put a string of icons to link to their FB, Twitter, etc. |
280
|
|
|
* This could still be done via conditional, so that administration and moderation notices were still active when |
281
|
|
|
* applicable. |
282
|
|
|
*/ |
283
|
|
|
function template_th_header_bar() |
284
|
|
|
{ |
285
|
|
|
global $context, $txt, $scripturl; |
286
|
|
|
|
287
|
|
|
echo ' |
288
|
|
|
<div id="top_section_notice" class="user', (empty($context['minmax_preferences']['upshrink']) ? '' : ' hide'), '"> |
289
|
|
|
</div>'; |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Search bar form, expands to input form when search icon is clicked |
294
|
|
|
*/ |
295
|
|
|
function template_search_form() |
296
|
|
|
{ |
297
|
|
|
global $context, $modSettings, $txt; |
298
|
|
|
|
299
|
|
|
echo ' |
300
|
|
|
<form id="search_form_menu" action="', getUrl('action', ['action' => 'search', 'sa' => 'results']), '" method="post" role="search" accept-charset="UTF-8">'; |
301
|
|
|
|
302
|
|
|
// Using the quick search dropdown? |
303
|
|
|
if (!empty($modSettings['search_dropdown'])) |
304
|
|
|
{ |
305
|
|
|
$selected = empty($context['current_topic']) ? (!empty($context['current_board']) ? 'current_board' : 'all') : ('current_topic'); |
306
|
|
|
echo ' |
307
|
|
|
<label for="search_selection"> |
308
|
|
|
<select name="search_selection" id="search_selection" class="linklevel1" aria-label="search selection"> |
309
|
|
|
<option value="all"', ($selected === 'all' ? ' selected="selected"' : ''), '>', $txt['search_entireforum'], ' </option>'; |
310
|
|
|
|
311
|
|
|
// Can't limit it to a specific topic if we are not in one |
312
|
|
|
if (!empty($context['current_topic'])) |
313
|
|
|
{ |
314
|
|
|
echo ' |
315
|
|
|
<option value="topic"', ($selected === 'current_topic' ? ' selected="selected"' : ''), '>', $txt['search_thistopic'], '</option>'; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
// Can't limit it to a specific board if we are not in one |
319
|
|
|
if (!empty($context['current_board'])) |
320
|
|
|
{ |
321
|
|
|
echo ' |
322
|
|
|
<option value="board"', ($selected === 'current_board' ? ' selected="selected"' : ''), '>', $txt['search_thisbrd'], '</option>'; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
if (!empty($context['additional_dropdown_search'])) |
326
|
|
|
{ |
327
|
|
|
foreach ($context['additional_dropdown_search'] as $name => $engine) |
328
|
|
|
{ |
329
|
|
|
echo ' |
330
|
|
|
<option value="', $name, '">', $engine['name'], '</option>'; |
331
|
|
|
} |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
echo ' |
335
|
|
|
<option value="members"', ($selected === 'members' ? ' selected="selected"' : ''), '>', $txt['search_members'], ' </option> |
336
|
|
|
</select> |
337
|
|
|
</label>'; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
// Search within current topic? |
341
|
|
|
if (!empty($context['current_topic'])) |
342
|
|
|
{ |
343
|
|
|
echo ' |
344
|
|
|
<input type="hidden" name="', (empty($modSettings['search_dropdown']) ? 'topic' : 'sd_topic'), '" value="', $context['current_topic'], '" />'; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
// If we're on a certain board, limit it to this board ;). |
348
|
|
|
if (!empty($context['current_board'])) |
349
|
|
|
{ |
350
|
|
|
echo ' |
351
|
|
|
<input type="hidden" name="', (empty($modSettings['search_dropdown']) ? 'brd[' : 'sd_brd['), $context['current_board'], ']"', ' value="', $context['current_board'], '" />'; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
echo ' |
355
|
|
|
<label for="quicksearch" class="hide">', $txt['search'], '</label> |
356
|
|
|
<input type="search" name="search" id="quicksearch" value="" class="linklevel1" placeholder="', $txt['search'], '" /> |
357
|
|
|
<button type="submit" aria-label="' . $txt['search'] . '" name="search;sa=results" class="', (empty($modSettings['search_dropdown'])) ? '' : 'with_select', '"> |
358
|
|
|
<i class="icon i-search"><s>', $txt['search'], '</s></i> |
359
|
|
|
</button> |
360
|
|
|
<button type="button" aria-label="' . $txt['find_close'] . '"> |
361
|
|
|
<label for="search_form_check"> |
362
|
|
|
<i class="icon i-close"><s>', $txt['find_close'], '</s></i> |
363
|
|
|
</label> |
364
|
|
|
</button> |
365
|
|
|
<input type="hidden" name="advanced" value="0" /> |
366
|
|
|
</form>'; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* Search bar main menu icon |
371
|
|
|
*/ |
372
|
|
|
function template_mb_search_bar() |
373
|
|
|
{ |
374
|
|
|
global $txt; |
375
|
|
|
|
376
|
|
|
echo ' |
377
|
|
|
<li id="search_form_button" class="listlevel1" role="none"> |
378
|
|
|
<label for="search_form_check"> |
379
|
|
|
<a class="linklevel1 panel_search" role="menuitem"> |
380
|
|
|
<i class="main-menu-icon i-search colorize-white"><s>', $txt['search'], '</s></i> |
381
|
|
|
</a> |
382
|
|
|
</label> |
383
|
|
|
</li>'; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* The news fader wrapped in a div and with "news" text |
388
|
|
|
*/ |
389
|
|
|
function template_uc_news_fader() |
390
|
|
|
{ |
391
|
|
|
global $settings, $context, $txt; |
392
|
|
|
|
393
|
|
|
// Display either news fader and random news lines (not both). These now run most of the same mark up and CSS. Less complication = happier n00bz. :) |
394
|
|
|
if (!empty($settings['enable_news']) && !empty($context['random_news_line'])) |
395
|
|
|
{ |
396
|
|
|
echo ' |
397
|
|
|
<div id="news"> |
398
|
|
|
<h2>', $txt['news'], '</h2>'; |
399
|
|
|
|
400
|
|
|
template_news_fader(); |
401
|
|
|
|
402
|
|
|
echo ' |
403
|
|
|
</div>'; |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Section down the page, before closing body |
409
|
|
|
*/ |
410
|
|
|
function template_body_below() |
411
|
|
|
{ |
412
|
|
|
global $context, $txt; |
413
|
|
|
|
414
|
|
|
echo ' |
415
|
|
|
</div> |
416
|
|
|
</div>'; |
417
|
|
|
|
418
|
|
|
// Show RSS link, as well as the copyright. |
419
|
|
|
// Footer is full-width. Wrapper inside automatically matches admin width setting. |
420
|
|
|
echo ' |
421
|
|
|
<footer id="footer_section"> |
422
|
|
|
<div class="wrapper"> |
423
|
|
|
<ul> |
424
|
|
|
<li class="copyright">', |
425
|
|
|
theme_copyright(), ' |
|
|
|
|
426
|
|
|
</li>', |
427
|
|
|
empty($context['newsfeed_urls']['rss']) ? '' : ' |
428
|
|
|
<li> |
429
|
|
|
<a id="button_rss" href="' . $context['newsfeed_urls']['rss'] . '" class="rssfeeds new_win"> |
430
|
|
|
<i class="icon icon-margin i-rss icon-big"><s>' . $txt['rss'] . '</s></i> |
431
|
|
|
</a> |
432
|
|
|
</li>', ' |
433
|
|
|
</ul>'; |
434
|
|
|
|
435
|
|
|
// Show the load time? |
436
|
|
|
if ($context['show_load_time']) |
437
|
|
|
{ |
438
|
|
|
echo ' |
439
|
|
|
<p>', sprintf($txt['page_created_full'], $context['load_time'], $context['load_queries']), '</p>'; |
440
|
|
|
} |
441
|
|
|
} |
442
|
|
|
|
443
|
|
|
/** |
444
|
|
|
* Section down the page, at closing html tag |
445
|
|
|
*/ |
446
|
|
|
function template_html_below() |
447
|
|
|
{ |
448
|
|
|
global $context; |
449
|
|
|
|
450
|
|
|
echo ' |
451
|
|
|
</div> |
452
|
|
|
</footer>'; |
453
|
|
|
|
454
|
|
|
// This is here to catch any late loading of JS files via templates |
455
|
|
|
theme()->themeJs()->outputJavascriptFiles(theme()->themeJs()->getJSFiles()); |
456
|
|
|
|
457
|
|
|
// load inline javascript that needed to be deferred to the end of the page |
458
|
|
|
theme()->themeJs()->template_inline_javascript(true); |
459
|
|
|
|
460
|
|
|
// Schema microdata about the organization? |
461
|
|
|
if (!empty($context['smd_site'])) |
462
|
|
|
{ |
463
|
|
|
echo ' |
464
|
|
|
<script type="application/ld+json"> |
465
|
|
|
', json_encode($context['smd_site'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), ' |
466
|
|
|
</script>'; |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
// Schema microdata about the post? |
470
|
|
|
if (!empty($context['smd_article'])) |
471
|
|
|
{ |
472
|
|
|
echo ' |
473
|
|
|
<script type="application/ld+json"> |
474
|
|
|
', json_encode($context['smd_article'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), ' |
475
|
|
|
</script>'; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
// Anything special to put out? |
479
|
|
|
if (!empty($context['insert_after_template'])) |
480
|
|
|
{ |
481
|
|
|
echo $context['insert_after_template']; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
echo ' |
485
|
|
|
</body> |
486
|
|
|
</html>'; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* Show breadcrumbs. This is that thing that shows |
491
|
|
|
* "My Community | General Category | General Discussion".. |
492
|
|
|
* |
493
|
|
|
* @param string $default a string representing the index in $context where |
494
|
|
|
* the breadcrumbs are stored (default value is 'breadcrumbs') |
495
|
|
|
*/ |
496
|
|
|
function theme_breadcrumbs($default = 'breadcrumbs') |
497
|
|
|
{ |
498
|
|
|
global $context, $settings, $txt; |
499
|
|
|
|
500
|
|
|
// If breadcrumbs is empty, just return - also allow an override. |
501
|
|
|
if (empty($context[$default])) |
502
|
|
|
{ |
503
|
|
|
return; |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
echo ' |
507
|
|
|
<nav class="breadcrumb" aria-label="breadcrumbs">'; |
508
|
|
|
|
509
|
|
|
// Each tree item has a URL and name. Some may have extra_before and extra_after. |
510
|
|
|
// Added a crumb class to make targeting dividers easy. |
511
|
|
|
foreach ($context[$default] as $pos => $tree) |
512
|
|
|
{ |
513
|
|
|
$tree['name'] = ($tree['extra_before'] ?? '') . $tree['name'] . ($tree['extra_after'] ?? ''); |
514
|
|
|
|
515
|
|
|
// Show the link, including a URL if it should have one. |
516
|
|
|
echo isset($tree['url']) |
517
|
|
|
? ' |
518
|
|
|
<span class="crumb"> |
519
|
|
|
<a href="' . $tree['url'] . '">' . |
520
|
|
|
($pos === 0 |
521
|
|
|
? '<i class="icon i-home"><s>' . $txt['home'] . '</s></i>' |
522
|
|
|
: $tree['name']) . ' |
523
|
|
|
</a> |
524
|
|
|
</span>' |
525
|
|
|
: ' |
526
|
|
|
<span class="crumb"> |
527
|
|
|
' . $tree['name'] . ' |
528
|
|
|
</span>'; |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
echo ' |
532
|
|
|
</nav>'; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Show the menu up top. Something like [home] [help] [profile] [logout]... |
537
|
|
|
*/ |
538
|
|
|
function template_menu() |
539
|
|
|
{ |
540
|
|
|
global $context, $txt; |
541
|
|
|
|
542
|
|
|
// WAI-ARIA a11y tweaks have been applied here. |
543
|
|
|
echo ' |
544
|
|
|
|
545
|
|
|
<nav id="menu_nav" aria-label="', $txt['main_menu'], '"> |
546
|
|
|
<div class="wrapper no_js"> |
547
|
|
|
<input type="checkbox" id="search_form_check" aria-hidden="true" /> |
548
|
|
|
<ul id="main_menu" aria-label="', $txt['main_menu'], '" role="menubar">'; |
549
|
|
|
|
550
|
|
|
// Add any additional menu buttons from addons |
551
|
|
|
call_template_callbacks('mb', $context['theme_header_callbacks']); |
552
|
|
|
|
553
|
|
|
// This defines the start of right aligned buttons, simply set your button order > 10 |
554
|
|
|
echo ' |
555
|
|
|
<li id="button_none" class="listlevel1" role="none"> |
556
|
|
|
<a role="none"></a> |
557
|
|
|
</li>'; |
558
|
|
|
|
559
|
|
|
// The upshrink image. |
560
|
|
|
echo ' |
561
|
|
|
<li id="collapse_button" class="listlevel1" role="none"> |
562
|
|
|
<a class="linklevel1 panel_toggle" role="menuitem"> |
563
|
|
|
<i id="upshrink" class="hide main-menu-icon i-chevron-up" title="', $txt['upshrink_description'], '"></i> |
564
|
|
|
</a> |
565
|
|
|
</li>'; |
566
|
|
|
|
567
|
|
|
// Now all the buttons from menu.subs |
568
|
|
|
foreach ($context['menu_buttons'] as $act => $button) |
569
|
|
|
{ |
570
|
|
|
// Top link details, easier to maintain broken out |
571
|
|
|
$class = 'class="linklevel1' . (empty($button['active_button']) ? '' : ' active') . (empty($button['indicator']) ? '' : ' indicator') . '"'; |
572
|
|
|
$href = ' href="' . $button['href'] . '"'; |
573
|
|
|
$target = isset($button['target']) ? ' target="' . $button['target'] . '"' : ''; |
574
|
|
|
$onclick = isset($button['onclick']) ? ' onclick="' . $button['onclick'] . '"' : ''; |
575
|
|
|
$altTitle = 'title="' . (empty($button['alttitle']) ? $button['title'] : $button['alttitle']) . '"'; |
576
|
|
|
$ally = empty($button['active_button']) ? '' : ' aria-current="page"'; |
577
|
|
|
|
578
|
|
|
echo ' |
579
|
|
|
<li id="button_', $act, '" class="listlevel1', empty($button['sub_buttons']) ? '"' : ' subsections"', ' role="none"> |
580
|
|
|
<a ', $class, $href, $target, $ally, $onclick, ' role="menuitem"', empty($button['sub_buttons']) ? '' : ' aria-haspopup="true"', '>', |
581
|
|
|
(empty($button['data-icon']) ? '' : '<i class="icon icon-menu icon-lg ' . $button['data-icon'] . (empty($button['active_button']) ? '' : ' enabled') . '" ' . $altTitle . '></i> '), |
582
|
|
|
'<span class="button_title" aria-hidden="', (empty($button['sub_buttons']) ? 'false' : 'true'), '">', $button['title'], '</span> |
583
|
|
|
</a>'; |
584
|
|
|
|
585
|
|
|
// Any 2nd level menus? |
586
|
|
|
if (!empty($button['sub_buttons'])) |
587
|
|
|
{ |
588
|
|
|
echo ' |
589
|
|
|
<ul class="menulevel2" role="menu">'; |
590
|
|
|
|
591
|
|
|
foreach ($button['sub_buttons'] as $childact => $childbutton) |
592
|
|
|
{ |
593
|
|
|
echo ' |
594
|
|
|
<li id="button_', $childact, '" class="listlevel2', empty($childbutton['sub_buttons']) ? '"' : ' subsections"', ' role="none"> |
595
|
|
|
<a class="linklevel2" href="', $childbutton['href'], '" ', isset($childbutton['target']) ? 'target="' . $childbutton['target'] . '"' : '', isset($childbutton['onclick']) ? ' onclick="' . $childbutton['onclick'] . '"' : '', empty($childbutton['sub_buttons']) ? '' : ' aria-haspopup="true"', ' role="menuitem">', |
596
|
|
|
$childbutton['title'], ' |
597
|
|
|
</a>'; |
598
|
|
|
|
599
|
|
|
// 3rd level menus :) |
600
|
|
|
if (!empty($childbutton['sub_buttons'])) |
601
|
|
|
{ |
602
|
|
|
echo ' |
603
|
|
|
<ul class="menulevel3" role="menu">'; |
604
|
|
|
|
605
|
|
|
foreach ($childbutton['sub_buttons'] as $grandchildact => $grandchildbutton) |
606
|
|
|
{ |
607
|
|
|
echo ' |
608
|
|
|
<li id="button_', $grandchildact, '" class="listlevel3" role="none"> |
609
|
|
|
<a class="linklevel3" href="', $grandchildbutton['href'], '" ', isset($grandchildbutton['target']) ? 'target="' . $grandchildbutton['target'] . '"' : '', isset($grandchildbutton['onclick']) ? ' onclick="' . $grandchildbutton['onclick'] . '"' : '', ' role="menuitem">', |
610
|
|
|
$grandchildbutton['title'], ' |
611
|
|
|
</a> |
612
|
|
|
</li>'; |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
echo ' |
616
|
|
|
</ul>'; |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
echo ' |
620
|
|
|
</li>'; |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
echo ' |
624
|
|
|
</ul>'; |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
echo ' |
628
|
|
|
</li>'; |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
echo ' |
632
|
|
|
|
633
|
|
|
</ul>'; |
634
|
|
|
|
635
|
|
|
// If search is enabled, plop in the form |
636
|
|
|
if ($context['allow_search']) |
637
|
|
|
{ |
638
|
|
|
template_search_form(); |
639
|
|
|
} |
640
|
|
|
|
641
|
|
|
echo ' </div> |
642
|
|
|
</nav>'; |
643
|
|
|
|
644
|
|
|
// Define the upper_section toggle in javascript. |
645
|
|
|
theme()->themeJs()->addInlineJavascript(' |
646
|
|
|
var oMainHeaderToggle = new elk_Toggle({ |
647
|
|
|
bToggleEnabled: true, |
648
|
|
|
bCurrentlyCollapsed: ' . (empty($context['minmax_preferences']['upshrink']) ? 'false' : 'true') . ', |
649
|
|
|
aSwappableContainers: [ |
650
|
|
|
\'upper_section\',\'header\',\'top_header\' |
651
|
|
|
], |
652
|
|
|
aSwapClasses: [ |
653
|
|
|
{ |
654
|
|
|
sId: \'upshrink\', |
655
|
|
|
classExpanded: \'chevricon i-chevron-up icon-lg\', |
656
|
|
|
titleExpanded: ' . JavaScriptEscape($txt['upshrink_description']) . ', |
657
|
|
|
classCollapsed: \'chevricon i-chevron-down icon-lg\', |
658
|
|
|
titleCollapsed: ' . JavaScriptEscape($txt['upshrink_description']) . ' |
659
|
|
|
}, |
660
|
|
|
], |
661
|
|
|
oThemeOptions: { |
662
|
|
|
bUseThemeSettings: ' . ($context['user']['is_guest'] ? 'false' : 'true') . ', |
663
|
|
|
sOptionName: \'minmax_preferences\', |
664
|
|
|
sSessionId: elk_session_id, |
665
|
|
|
sSessionVar: elk_session_var, |
666
|
|
|
sAdditionalVars: \';minmax_key=upshrink\' |
667
|
|
|
}, |
668
|
|
|
oCookieOptions: { |
669
|
|
|
bUseCookie: elk_member_id == 0 ? true : false, |
670
|
|
|
sCookieName: \'upshrink\' |
671
|
|
|
}, |
672
|
|
|
funcOnBeforeCollapse: function () { |
673
|
|
|
let header = document.getElementById(\'top_section\'); |
674
|
|
|
header.classList.add(\'th_collapse\'); |
675
|
|
|
header.classList.remove(\'th_expand\'); |
676
|
|
|
}, |
677
|
|
|
funcOnBeforeExpand: function () { |
678
|
|
|
let header = document.getElementById(\'top_section\'); |
679
|
|
|
header.classList.add(\'th_expand\'); |
680
|
|
|
header.classList.remove(\'th_collapse\'); |
681
|
|
|
}, |
682
|
|
|
}); |
683
|
|
|
', true); |
684
|
|
|
} |
685
|
|
|
|
686
|
|
|
/** |
687
|
|
|
* Very simple and basic template to display a legend explaining the meaning |
688
|
|
|
* of some icons used in the messages listing (locked, sticky, etc.) |
689
|
|
|
*/ |
690
|
|
|
function template_basicicons_legend() |
691
|
|
|
{ |
692
|
|
|
global $context, $modSettings, $txt; |
693
|
|
|
|
694
|
|
|
echo ' |
695
|
|
|
<p class="floatleft">', !empty($modSettings['enableParticipation']) && $context['user']['is_logged'] ? ' |
696
|
|
|
<span class="topicicon i-profile"></span> ' . $txt['participation_caption'] : '<span class="topicicon img_normal"> </span>' . $txt['normal_topic'], '<br /> |
697
|
|
|
' . (empty($modSettings['pollMode']) ? '' : '<span class="topicicon i-poll"> </span>' . $txt['poll']) . ' |
698
|
|
|
</p> |
699
|
|
|
<p> |
700
|
|
|
<span class="topicicon i-locked"> </span>' . $txt['locked_topic'] . '<br /> |
701
|
|
|
<span class="topicicon i-sticky"> </span>' . $txt['sticky_topic'] . '<br /> |
702
|
|
|
</p>'; |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* Show a box with a message, mostly used to show errors, but can be used to show |
707
|
|
|
* success as well |
708
|
|
|
* |
709
|
|
|
* Looks for the display information in the $context[$error_id] array |
710
|
|
|
* Keys of array are 'type' |
711
|
|
|
* - empty or success for successbox |
712
|
|
|
* - serious for error box |
713
|
|
|
* - warning for warning box |
714
|
|
|
* 'title' - optional value to place above list |
715
|
|
|
* 'errors' - array of text strings to display in the box |
716
|
|
|
* |
717
|
|
|
* @param string $error_id |
718
|
|
|
*/ |
719
|
|
|
function template_show_error($error_id) |
720
|
|
|
{ |
721
|
|
|
global $context; |
722
|
|
|
|
723
|
|
|
if (empty($error_id)) |
724
|
|
|
{ |
725
|
|
|
return; |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
$error = $context[$error_id] ?? array(); |
729
|
|
|
|
730
|
|
|
echo ' |
731
|
|
|
<div id="', $error_id, '" class="', (isset($error['type']) ? ($error['type'] === 'serious' ? 'errorbox' : 'warningbox') : 'successbox'), empty($error['errors']) ? ' hide"' : '"', '>'; |
732
|
|
|
|
733
|
|
|
// Optional title for our results |
734
|
|
|
if (!empty($error['title'])) |
735
|
|
|
{ |
736
|
|
|
echo ' |
737
|
|
|
<dl> |
738
|
|
|
<dt> |
739
|
|
|
<strong id="', $error_id, '_title">', $error['title'], '</strong> |
740
|
|
|
</dt> |
741
|
|
|
<dd>'; |
742
|
|
|
} |
743
|
|
|
|
744
|
|
|
// Everything that went wrong, or correctly :) |
745
|
|
|
if (!empty($error['errors'])) |
746
|
|
|
{ |
747
|
|
|
echo ' |
748
|
|
|
<ul', (isset($error['type']) ? ' class="error"' : ''), ' id="', $error_id, '_list">'; |
749
|
|
|
|
750
|
|
|
foreach ($error['errors'] as $key => $err) |
751
|
|
|
{ |
752
|
|
|
echo ' |
753
|
|
|
<li id="', $error_id, '_', $key, '">', $err, '</li>'; |
754
|
|
|
} |
755
|
|
|
|
756
|
|
|
echo ' |
757
|
|
|
</ul>'; |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
// All done |
761
|
|
|
if (!empty($error['title'])) |
762
|
|
|
{ |
763
|
|
|
echo ' |
764
|
|
|
</dd> |
765
|
|
|
</dl>'; |
766
|
|
|
} |
767
|
|
|
|
768
|
|
|
echo ' |
769
|
|
|
</div>'; |
770
|
|
|
} |
771
|
|
|
|
772
|
|
|
/** |
773
|
|
|
* Is this used? |
774
|
|
|
*/ |
775
|
|
|
function template_uc_generic_infobox() |
776
|
|
|
{ |
777
|
|
|
global $context; |
778
|
|
|
|
779
|
|
|
if (empty($context['generic_infobox'])) |
780
|
|
|
{ |
781
|
|
|
return; |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
foreach ($context['generic_infobox'] as $key) |
785
|
|
|
{ |
786
|
|
|
template_show_error($key); |
787
|
|
|
} |
788
|
|
|
} |
789
|
|
|
|
790
|
|
|
/** |
791
|
|
|
* This is the news fader |
792
|
|
|
*/ |
793
|
|
|
function template_news_fader() |
794
|
|
|
{ |
795
|
|
|
global $settings, $context; |
796
|
|
|
|
797
|
|
|
echo ' |
798
|
|
|
<ul id="elkFadeScroller"> |
799
|
|
|
<li> |
800
|
|
|
', $settings['enable_news'] == 2 ? implode('</li><li>', $context['news_lines']) : $context['random_news_line'], ' |
801
|
|
|
</li> |
802
|
|
|
</ul> |
803
|
|
|
<script type="module"> |
804
|
|
|
Elk_NewsFader("elkFadeScroller", {' . (empty($settings['newsfader_time']) ? '' : 'iFadeDelay: ' . $settings['newsfader_time']) . '}) |
805
|
|
|
</script>'; |
806
|
|
|
} |
807
|
|
|
|
808
|
|
|
/** |
809
|
|
|
* |
810
|
|
|
* @TODO: These need to be moved somewhere appropriate >_> |
811
|
|
|
* |
812
|
|
|
* @param array $member |
813
|
|
|
* @param bool $link |
814
|
|
|
* |
815
|
|
|
* @return string |
816
|
|
|
*/ |
817
|
|
|
function template_member_online($member, $link = true) |
818
|
|
|
{ |
819
|
|
|
global $context; |
820
|
|
|
|
821
|
|
|
return ((!empty($context['can_send_pm']) && $link) ? '<a href="' . $member['online']['href'] . '" title="' . $member['online']['text'] . '">' : '') . |
822
|
|
|
'<i class="' . ($member['online']['is_online'] ? 'iconline' : 'icoffline') . '" title="' . $member['online']['text'] . '"></i>' . |
823
|
|
|
((!empty($context['can_send_pm']) && $link) ? '</a>' : ''); |
824
|
|
|
} |
825
|
|
|
|
826
|
|
|
/** |
827
|
|
|
* Similar to the above. Wanted to centralize this to make it easier to pull out the emailuser action and replace with |
828
|
|
|
* a mailto: href, which many sane board admins would prefer. |
829
|
|
|
* |
830
|
|
|
* @param array $member |
831
|
|
|
* @param bool $text |
832
|
|
|
* |
833
|
|
|
* @return string |
834
|
|
|
*/ |
835
|
|
|
function template_member_email($member, $text = false) |
836
|
|
|
{ |
837
|
|
|
global $context, $txt; |
838
|
|
|
|
839
|
|
|
if ($context['can_send_email']) |
840
|
|
|
{ |
841
|
|
|
if ($text) |
842
|
|
|
{ |
843
|
|
|
if ($member !== false && $member['show_email']) |
844
|
|
|
{ |
845
|
|
|
return '<a class="linkbutton" href="mailto:' . $member['email'] . '" rel="nofollow">' . $txt['email'] . '</a>'; |
846
|
|
|
} |
847
|
|
|
|
848
|
|
|
return $txt['hidden']; |
849
|
|
|
} |
850
|
|
|
|
851
|
|
|
if ($member !== false && $member['show_email']) |
852
|
|
|
{ |
853
|
|
|
return '<a href="mailto:' . $member['email'] . '" rel="nofollow" class="icon i-envelope-o' . ($member['online']['is_online'] ? '' : '-blank') . '" title="' . $txt['email'] . ' ' . $member['name'] . '"><s>' . $txt['email'] . ' ' . $member['name'] . '</s></a>'; |
854
|
|
|
} |
855
|
|
|
|
856
|
|
|
return '<i class="icon i-envelope-o" title="' . $txt['email'] . ' ' . $txt['hidden'] . '"><s>' . $txt['email'] . ' ' . $txt['hidden'] . '</s></i>'; |
857
|
|
|
} |
858
|
|
|
|
859
|
|
|
return ''; |
860
|
|
|
} |
861
|
|
|
|