1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file has the hefty job of loading information for the forum. |
5
|
|
|
* |
6
|
|
|
* @package ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
9
|
|
|
* |
10
|
|
|
* This file contains code covered by: |
11
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
12
|
|
|
* |
13
|
|
|
* @version 2.0 dev |
14
|
|
|
* |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
use BBC\ParserWrapper; |
18
|
|
|
use ElkArte\Attachments\AttachmentsDirectory; |
19
|
|
|
use ElkArte\Cache\Cache; |
20
|
|
|
use ElkArte\Debug; |
21
|
|
|
use ElkArte\Errors\Errors; |
22
|
|
|
use ElkArte\Helper\FileFunctions; |
23
|
|
|
use ElkArte\Helper\Util; |
24
|
|
|
use ElkArte\Hooks; |
25
|
|
|
use ElkArte\Http\Headers; |
26
|
|
|
use ElkArte\Languages\Txt; |
27
|
|
|
use ElkArte\Server; |
28
|
|
|
use ElkArte\Themes\ThemeLoader; |
29
|
|
|
use ElkArte\User; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Load the $modSettings array and many necessary forum settings. |
33
|
|
|
* |
34
|
|
|
* What it does: |
35
|
|
|
* |
36
|
|
|
* - load the settings from cache if available, otherwise from the database. |
37
|
|
|
* - sets the timezone |
38
|
|
|
* - checks the load average settings if available. |
39
|
|
|
* - check whether post moderation is enabled. |
40
|
|
|
* - calls add_integration_function |
41
|
|
|
* - calls integrate_pre_include, integrate_pre_load, |
42
|
|
|
* |
43
|
|
|
* @event integrate_load_average is called if load average is enabled |
44
|
|
|
* @event integrate_pre_include to allow including files at startup |
45
|
|
|
* @event integrate_pre_load to call any pre load integration functions. |
46
|
1 |
|
* |
47
|
|
|
* @global array $modSettings is a giant array of all the forum-wide settings and statistics. |
48
|
1 |
|
*/ |
49
|
1 |
|
function reloadSettings() |
50
|
1 |
|
{ |
51
|
|
|
global $modSettings; |
52
|
|
|
|
53
|
1 |
|
$db = database(); |
54
|
|
|
$cache = Cache::instance(); |
55
|
1 |
|
$hooks = Hooks::instance(); |
56
|
|
|
|
57
|
|
|
// Try to load it from the cache first; it'll never get cached if the setting is off. |
58
|
1 |
|
if (!$cache->getVar($modSettings, 'modSettings', 90)) |
59
|
|
|
{ |
60
|
1 |
|
$request = $db->query('', ' |
61
|
1 |
|
SELECT |
62
|
|
|
variable, value |
63
|
|
|
FROM {db_prefix}settings', |
64
|
|
|
array() |
65
|
1 |
|
); |
66
|
|
|
$modSettings = array(); |
67
|
1 |
|
if (!$request) |
68
|
|
|
{ |
69
|
1 |
|
Errors::instance()->display_db_error(); |
70
|
|
|
} |
71
|
|
|
|
72
|
1 |
|
while (($row = $request->fetch_row())) |
73
|
|
|
{ |
74
|
|
|
$modSettings[$row[0]] = $row[1]; |
75
|
|
|
} |
76
|
|
|
|
77
|
1 |
|
$request->free_result(); |
78
|
|
|
|
79
|
|
|
// Do a few things to protect against missing settings or settings with invalid values... |
80
|
|
|
if (empty($modSettings['defaultMaxTopics']) || $modSettings['defaultMaxTopics'] <= 0 || $modSettings['defaultMaxTopics'] > 999) |
81
|
|
|
{ |
82
|
1 |
|
$modSettings['defaultMaxTopics'] = 20; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
if (empty($modSettings['defaultMaxMessages']) || $modSettings['defaultMaxMessages'] <= 0 || $modSettings['defaultMaxMessages'] > 999) |
86
|
|
|
{ |
87
|
1 |
|
$modSettings['defaultMaxMessages'] = 15; |
88
|
|
|
} |
89
|
1 |
|
|
90
|
|
|
if (empty($modSettings['defaultMaxMembers']) || $modSettings['defaultMaxMembers'] <= 0 || $modSettings['defaultMaxMembers'] > 999) |
91
|
|
|
{ |
92
|
1 |
|
$modSettings['defaultMaxMembers'] = 30; |
93
|
|
|
} |
94
|
1 |
|
|
95
|
|
|
$modSettings['warning_enable'] = $modSettings['warning_settings'][0]; |
96
|
|
|
|
97
|
1 |
|
$cache->put('modSettings', $modSettings, 90); |
98
|
|
|
} |
99
|
|
|
|
100
|
1 |
|
$hooks->loadIntegrations(); |
101
|
|
|
|
102
|
1 |
|
// Setting the timezone is a requirement for some functions in PHP >= 5.1. |
103
|
|
|
if (isset($modSettings['default_timezone'])) |
104
|
|
|
{ |
105
|
|
|
date_default_timezone_set($modSettings['default_timezone']); |
106
|
1 |
|
} |
107
|
|
|
|
108
|
|
|
// Check the load averages? |
109
|
|
|
if (!empty($modSettings['loadavg_enable'])) |
110
|
|
|
{ |
111
|
|
|
if (!$cache->getVar($modSettings['load_average'], 'loadavg', 90)) |
112
|
|
|
{ |
113
|
|
|
require_once(SUBSDIR . '/Server.subs.php'); |
114
|
|
|
$modSettings['load_average'] = detectServerLoad(); |
115
|
|
|
|
116
|
|
|
$cache->put('loadavg', $modSettings['load_average'], 90); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
if ($modSettings['load_average'] !== false) |
120
|
|
|
{ |
121
|
|
|
call_integration_hook('integrate_load_average', array($modSettings['load_average'])); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
// Let's have at least a zero |
125
|
|
|
if (empty($modSettings['loadavg_forum']) || $modSettings['load_average'] === false) |
126
|
|
|
{ |
127
|
|
|
$modSettings['current_load'] = 0; |
128
|
|
|
} |
129
|
|
|
else |
130
|
|
|
{ |
131
|
|
|
$modSettings['current_load'] = $modSettings['load_average']; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
if (!empty($modSettings['loadavg_forum']) && $modSettings['current_load'] >= $modSettings['loadavg_forum']) |
135
|
|
|
{ |
136
|
|
|
Errors::instance()->display_loadavg_error(); |
137
|
|
|
} |
138
|
1 |
|
} |
139
|
|
|
else |
140
|
|
|
{ |
141
|
|
|
$modSettings['current_load'] = 0; |
142
|
1 |
|
} |
143
|
|
|
|
144
|
1 |
|
// Is post moderation alive and well? |
145
|
|
|
$modSettings['postmod_active'] = !isset($modSettings['admin_features']) || in_array('pm', explode(',', $modSettings['admin_features'])); |
146
|
1 |
|
|
147
|
|
|
if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off') |
148
|
|
|
{ |
149
|
|
|
$modSettings['secureCookies'] = 0; |
150
|
1 |
|
} |
151
|
|
|
|
152
|
|
|
// Integration is cool. |
153
|
|
|
if (defined('ELK_INTEGRATION_SETTINGS')) |
154
|
|
|
{ |
155
|
|
|
$integration_settings = Util::unserialize(ELK_INTEGRATION_SETTINGS); |
156
|
|
|
foreach ($integration_settings as $hook => $function) |
157
|
|
|
{ |
158
|
|
|
add_integration_function($hook, $function); |
159
|
|
|
} |
160
|
1 |
|
} |
161
|
|
|
|
162
|
|
|
// Any files to pre include? |
163
|
1 |
|
call_integration_include_hook('integrate_pre_include'); |
164
|
1 |
|
|
165
|
|
|
// Call pre load integration functions. |
166
|
|
|
call_integration_hook('integrate_pre_load'); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Load all the important user information. |
171
|
|
|
* |
172
|
|
|
* What it does: |
173
|
|
|
* |
174
|
|
|
* - sets up the $user_info array |
175
|
|
|
* - assigns $user_info['query_wanna_see_board'] for what boards the user can see. |
176
|
|
|
* - first checks for cookie or integration validation. |
177
|
|
|
* - uses the current session if no integration function or cookie is found. |
178
|
|
|
* - checks password length, if member is activated and the login span isn't over. |
179
|
|
|
* - if validation fails for the user, $id_member is set to 0. |
180
|
|
|
* - updates the last visit time when needed. |
181
|
|
|
* |
182
|
|
|
* @event integrate_verify_user allow for integration to verify a user |
183
|
|
|
* @event integrate_user_info to allow for adding to $user_info array |
184
|
|
|
* @deprecated kept until any trace of $user_info has been completely removed |
185
|
1 |
|
*/ |
186
|
1 |
|
function loadUserSettings() |
187
|
|
|
{ |
188
|
|
|
User::load(true); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Check for moderators and see if they have access to the board. |
193
|
|
|
* |
194
|
|
|
* What it does: |
195
|
|
|
* |
196
|
|
|
* - sets up the $board_info array for current board information. |
197
|
|
|
* - if cache is enabled, the $board_info array is stored in cache. |
198
|
|
|
* - redirects to appropriate post if only message id is requested. |
199
|
|
|
* - is only used when inside a topic or board. |
200
|
|
|
* - determines the local moderators for the board. |
201
|
|
|
* - adds group id 3 if the user is a local moderator for the board they are in. |
202
|
|
|
* - prevents access if user is not in proper group nor a local moderator of the board. |
203
|
|
|
* |
204
|
|
|
* @event integrate_load_board_query allows to add tables and columns to the query, used |
205
|
|
|
* to add to the $board_info array |
206
|
|
|
* @event integrate_loaded_board called after board_info is populated, allows to add |
207
|
|
|
* directly to $board_info |
208
|
|
|
*/ |
209
|
13 |
|
function loadBoard() |
210
|
13 |
|
{ |
211
|
|
|
global $txt, $scripturl, $context, $modSettings, $board_info, $board, $topic; |
212
|
13 |
|
|
213
|
13 |
|
$db = database(); |
214
|
|
|
$cache = Cache::instance(); |
215
|
|
|
|
216
|
13 |
|
// Assume they are not a moderator. |
217
|
|
|
User::$info->is_mod = false; |
218
|
|
|
// @since 1.0.5 - is_mod takes into account only local (board) moderators, |
219
|
13 |
|
// and not global moderators, is_moderator is meant to take into account both. |
220
|
|
|
User::$info->is_moderator = false; |
221
|
|
|
|
222
|
13 |
|
// Start the breadcrumbs off empty.. |
223
|
|
|
$context['breadcrumbs'] = []; |
224
|
|
|
|
225
|
13 |
|
// Have they by chance specified a message id but nothing else? |
226
|
|
|
if (empty($_REQUEST['action']) && empty($topic) && empty($board) && !empty($_REQUEST['msg'])) |
227
|
|
|
{ |
228
|
|
|
// Make sure the message id is really an int. |
229
|
|
|
$_REQUEST['msg'] = (int) $_REQUEST['msg']; |
230
|
|
|
|
231
|
|
|
// Looking through the message table can be slow, so try using the cache first. |
232
|
|
|
if (!$cache->getVar($topic, 'msg_topic-' . $_REQUEST['msg'], 120)) |
233
|
|
|
{ |
234
|
|
|
require_once(SUBSDIR . '/Messages.subs.php'); |
235
|
|
|
$topic = associatedTopic($_REQUEST['msg']); |
236
|
|
|
|
237
|
|
|
// So did it find anything? |
238
|
|
|
if ($topic !== false) |
239
|
|
|
{ |
240
|
|
|
// Save save save. |
241
|
|
|
$cache->put('msg_topic-' . $_REQUEST['msg'], $topic, 120); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
// Remember redirection is the key to avoiding fallout from your bosses. |
246
|
|
|
if (!empty($topic)) |
247
|
|
|
{ |
248
|
|
|
redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg']); |
249
|
|
|
} |
250
|
|
|
else |
251
|
|
|
{ |
252
|
|
|
loadPermissions(); |
253
|
|
|
new ElkArte\Themes\ThemeLoader(); |
254
|
|
|
if (!empty(User::$info->possibly_robot)) |
255
|
|
|
{ |
256
|
|
|
Headers::instance() |
257
|
|
|
->removeHeader('all') |
258
|
13 |
|
->headerSpecial('HTTP/1.1 410 Gone') |
259
|
|
|
->sendHeaders(); |
260
|
1 |
|
} |
261
|
|
|
|
262
|
1 |
|
throw new \ElkArte\Exceptions\Exception('topic_gone', false); |
263
|
|
|
} |
264
|
|
|
} |
265
|
12 |
|
|
266
|
|
|
// Load this board only if it is specified. |
267
|
|
|
if (empty($board) && empty($topic)) |
268
|
|
|
{ |
269
|
|
|
$board_info = array('moderators' => array()); |
270
|
|
|
|
271
|
|
|
return; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2))) |
275
|
|
|
{ |
276
|
|
|
// @todo SLOW? |
277
|
|
|
$temp = empty($topic) ? $cache->get('board-' . $board, 120) : $cache->get('topic_board-' . $topic, 120); |
278
|
|
|
|
279
|
|
|
if (!empty($temp)) |
280
|
|
|
{ |
281
|
|
|
$board_info = $temp; |
282
|
|
|
$board = (int) $board_info['id']; |
283
|
|
|
} |
284
|
12 |
|
} |
285
|
|
|
|
286
|
12 |
|
if (empty($temp)) |
287
|
12 |
|
{ |
288
|
|
|
$select_columns = array(); |
289
|
12 |
|
$select_tables = array(); |
290
|
|
|
// Wanna grab something more from the boards table or another table at all? |
291
|
12 |
|
call_integration_hook('integrate_load_board_query', array(&$select_columns, &$select_tables)); |
292
|
|
|
|
293
|
|
|
$request = $db->query('', ' |
294
|
|
|
SELECT |
295
|
12 |
|
c.id_cat, b.name AS bname, b.description, b.num_topics, b.member_groups, b.deny_member_groups, |
296
|
|
|
b.id_parent, c.name AS cname, COALESCE(mem.id_member, 0) AS id_moderator, |
297
|
12 |
|
mem.real_name' . (empty($topic) ? '' : ', b.id_board') . ', b.child_level, |
298
|
12 |
|
b.id_theme, b.override_theme, b.count_posts, b.old_posts, b.id_profile, b.redirect, |
299
|
12 |
|
b.unapproved_topics, b.unapproved_posts' . (empty($topic) ? '' : ', t.approved, t.id_member_started') . (empty($select_columns) ? '' : ', ' . implode(', ', $select_columns)) . ' |
300
|
12 |
|
FROM {db_prefix}boards AS b' . (empty($topic) ? '' : ' |
301
|
|
|
INNER JOIN {db_prefix}topics AS t ON (t.id_topic = {int:current_topic})') . (empty($select_tables) ? '' : ' |
302
|
|
|
' . implode("\n\t\t\t\t", $select_tables)) . ' |
303
|
|
|
LEFT JOIN {db_prefix}categories AS c ON (c.id_cat = b.id_cat) |
304
|
|
|
LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = {raw:board_link}) |
305
|
|
|
LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) |
306
|
12 |
|
WHERE b.id_board = {raw:board_link}', |
307
|
12 |
|
array( |
308
|
|
|
'current_topic' => $topic, |
309
|
|
|
'board_link' => empty($topic) ? $db->quote('{int:current_board}', array('current_board' => $board)) : 't.id_board', |
310
|
|
|
) |
311
|
12 |
|
); |
312
|
|
|
// If there aren't any, skip. |
313
|
12 |
|
if ($request->num_rows() > 0) |
314
|
|
|
{ |
315
|
|
|
$row = $request->fetch_assoc(); |
316
|
12 |
|
|
317
|
|
|
// Set the current board. |
318
|
10 |
|
if (!empty($row['id_board'])) |
319
|
|
|
{ |
320
|
|
|
$board = (int) $row['id_board']; |
321
|
|
|
} |
322
|
|
|
|
323
|
12 |
|
// Basic operating information. (globals... :/) |
324
|
|
|
$board_info = array( |
325
|
|
|
'id' => $board, |
326
|
12 |
|
'moderators' => array(), |
327
|
12 |
|
'cat' => array( |
328
|
|
|
'id' => (int) $row['id_cat'], |
329
|
12 |
|
'name' => $row['cname'] |
330
|
12 |
|
), |
331
|
12 |
|
'name' => $row['bname'], |
332
|
12 |
|
'raw_description' => $row['description'], |
333
|
12 |
|
'description' => $row['description'], |
334
|
12 |
|
'num_topics' => (int) $row['num_topics'], |
335
|
12 |
|
'unapproved_topics' => (int) $row['unapproved_topics'], |
336
|
12 |
|
'unapproved_posts' => (int) $row['unapproved_posts'], |
337
|
12 |
|
'unapproved_user_topics' => 0, |
338
|
12 |
|
'parent_boards' => getBoardParents($row['id_parent']), |
339
|
12 |
|
'parent' => (int) $row['id_parent'], |
340
|
12 |
|
'child_level' => (int) $row['child_level'], |
341
|
12 |
|
'theme' => $row['id_theme'], |
342
|
12 |
|
'override_theme' => !empty($row['override_theme']), |
343
|
12 |
|
'profile' => (int) $row['id_profile'], |
344
|
12 |
|
'redirect' => $row['redirect'], |
345
|
12 |
|
'posts_count' => empty($row['count_posts']), |
346
|
|
|
'old_posts' => empty($row['old_posts']), |
347
|
|
|
'cur_topic_approved' => empty($topic) || $row['approved'], |
348
|
|
|
'cur_topic_starter' => empty($topic) ? 0 : $row['id_member_started'], |
349
|
12 |
|
); |
350
|
12 |
|
|
351
|
|
|
// Load the membergroups allowed, and check permissions. |
352
|
12 |
|
$board_info['groups'] = $row['member_groups'] === '' ? array() : explode(',', $row['member_groups']); |
353
|
|
|
$board_info['deny_groups'] = $row['deny_member_groups'] === '' ? array() : explode(',', $row['deny_member_groups']); |
354
|
|
|
|
355
|
|
|
call_integration_hook('integrate_loaded_board', array(&$board_info, &$row)); |
356
|
12 |
|
|
357
|
|
|
do |
358
|
|
|
{ |
359
|
|
|
if (!empty($row['id_moderator'])) |
360
|
|
|
{ |
361
|
|
|
$board_info['moderators'][$row['id_moderator']] = array( |
362
|
|
|
'id' => $row['id_moderator'], |
363
|
|
|
'name' => $row['real_name'], |
364
|
|
|
'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]), |
365
|
12 |
|
'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]) . '">' . $row['real_name'] . '</a>' |
366
|
|
|
); |
367
|
|
|
} |
368
|
|
|
} while (($row = $request->fetch_assoc())); |
369
|
12 |
|
|
370
|
|
|
// If the board only contains unapproved posts and the user can't approve then they can't see any topics. |
371
|
|
|
// If that is the case do an additional check to see if they have any topics waiting to be approved. |
372
|
|
|
if ($board_info['num_topics'] === 0 && $modSettings['postmod_active'] && !allowedTo('approve_posts')) |
373
|
|
|
{ |
374
|
|
|
// Free the previous result |
375
|
|
|
$request->free_result(); |
376
|
|
|
|
377
|
|
|
// @todo why is this using id_topic? |
378
|
|
|
// @todo Can this get cached? |
379
|
|
|
$request = $db->query('', ' |
380
|
|
|
SELECT COUNT(id_topic) |
381
|
|
|
FROM {db_prefix}topics |
382
|
|
|
WHERE id_member_started={int:id_member} |
383
|
|
|
AND approved = {int:unapproved} |
384
|
|
|
AND id_board = {int:board}', |
385
|
|
|
array( |
386
|
|
|
'id_member' => User::$info->id, |
387
|
|
|
'unapproved' => 0, |
388
|
|
|
'board' => $board, |
389
|
|
|
) |
390
|
|
|
); |
391
|
|
|
|
392
|
12 |
|
[$board_info['unapproved_user_topics']] = $request->fetch_row(); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
if ($cache->isEnabled() && (empty($topic) || $cache->levelHigherThan(2))) |
396
|
|
|
{ |
397
|
|
|
// @todo SLOW? |
398
|
|
|
if (!empty($topic)) |
399
|
|
|
{ |
400
|
12 |
|
$cache->put('topic_board-' . $topic, $board_info, 120); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
$cache->put('board-' . $board, $board_info, 120); |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
else |
407
|
|
|
{ |
408
|
|
|
// Otherwise the topic is invalid, there are no moderators, etc. |
409
|
|
|
$board_info = [ |
410
|
|
|
'moderators' => [], |
411
|
|
|
'error' => 'exist' |
412
|
|
|
]; |
413
|
12 |
|
$topic = null; |
414
|
|
|
$board = 0; |
415
|
|
|
} |
416
|
12 |
|
|
417
|
|
|
$request->free_result(); |
418
|
10 |
|
} |
419
|
|
|
|
420
|
|
|
if (!empty($topic)) |
421
|
12 |
|
{ |
422
|
|
|
$_GET['board'] = $board; |
423
|
|
|
} |
424
|
12 |
|
|
425
|
|
|
if (!empty($board)) |
426
|
12 |
|
{ |
427
|
|
|
// Now check if the user is a moderator. |
428
|
|
|
User::$info->is_mod = isset($board_info['moderators'][User::$info->id]); |
429
|
|
|
|
430
|
12 |
|
if (User::$info->is_admin === false && count(array_intersect(User::$info->groups, $board_info['groups'])) === 0) |
431
|
|
|
{ |
432
|
|
|
$board_info['error'] = 'access'; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
if (!empty($modSettings['deny_boards_access']) && count(array_intersect(User::$info->groups, $board_info['deny_groups'])) != 0 && User::$info->is_admin === false) |
436
|
12 |
|
{ |
437
|
12 |
|
$board_info['error'] = 'access'; |
438
|
|
|
} |
439
|
12 |
|
|
440
|
12 |
|
// Build up the breadcrumbs. |
441
|
|
|
$context['breadcrumbs'] = array_merge( |
442
|
|
|
$context['breadcrumbs'], |
443
|
12 |
|
[[ |
444
|
|
|
'url' => getUrl('action', $modSettings['default_forum_action']) . '#c' . $board_info['cat']['id'], |
445
|
12 |
|
'name' => $board_info['cat']['name'] |
446
|
12 |
|
] |
447
|
|
|
], |
448
|
|
|
array_reverse($board_info['parent_boards']), [ |
449
|
|
|
[ |
450
|
|
|
'url' => getUrl('board', ['board' => $board, 'start' => '0', 'name' => $board_info['name']]), |
451
|
|
|
'name' => $board_info['name'] |
452
|
|
|
] |
453
|
12 |
|
] |
454
|
12 |
|
); |
455
|
12 |
|
} |
456
|
12 |
|
|
457
|
|
|
// Set the template contextual information. |
458
|
|
|
$context['user']['is_mod'] = (bool) User::$info->is_mod; |
459
|
12 |
|
$context['user']['is_moderator'] = (bool) User::$info->is_moderator; |
460
|
|
|
$context['current_topic'] = $topic; |
461
|
|
|
$context['current_board'] = $board; |
462
|
|
|
|
463
|
|
|
// Hacker... you can't see this topic, I'll tell you that. (but moderators can!) |
464
|
|
|
if (!empty($board_info['error']) && (!empty($modSettings['deny_boards_access']) || $board_info['error'] !== 'access' || User::$info->is_moderator === false)) |
465
|
|
|
{ |
466
|
|
|
// The permissions and theme need loading, just to make sure everything goes smoothly. |
467
|
|
|
loadPermissions(); |
468
|
|
|
new ElkArte\Themes\ThemeLoader(); |
469
|
|
|
|
470
|
|
|
$_GET['board'] = ''; |
471
|
|
|
$_GET['topic'] = ''; |
472
|
|
|
|
473
|
|
|
// The breadcrumbs should not give the game away mate! |
474
|
|
|
$context['breadcrumbs'] = [ |
475
|
|
|
[ |
476
|
|
|
'url' => $scripturl, |
477
|
|
|
'name' => $context['forum_name_html_safe'] |
478
|
|
|
] |
479
|
|
|
]; |
480
|
|
|
|
481
|
|
|
// If it's a prefetching agent, stop it |
482
|
|
|
stop_prefetching(); |
483
|
|
|
|
484
|
|
|
// If we're requesting an attachment. |
485
|
|
|
if (!empty($_REQUEST['action']) && $_REQUEST['action'] === 'dlattach') |
486
|
|
|
{ |
487
|
|
|
ob_end_clean(); |
488
|
|
|
Headers::instance() |
489
|
|
|
->removeHeader('all') |
490
|
|
|
->headerSpecial('HTTP/1.1 403 Forbidden') |
491
|
|
|
->sendHeaders(); |
492
|
|
|
exit; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
if (User::$info->is_guest) |
496
|
|
|
{ |
497
|
12 |
|
Txt::load('Errors'); |
498
|
|
|
is_not_guest($txt['topic_gone']); |
499
|
|
|
} |
500
|
|
|
else |
501
|
12 |
|
{ |
502
|
|
|
if (!empty(User::$info->possibly_robot)) |
503
|
|
|
{ |
504
|
|
|
Headers::instance() |
505
|
|
|
->removeHeader('all') |
506
|
|
|
->headerSpecial('HTTP/1.1 410 Gone') |
507
|
|
|
->sendHeaders(); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
throw new \ElkArte\Exceptions\Exception('topic_gone', false); |
511
|
|
|
} |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
if (User::$info->is_mod) |
515
|
|
|
{ |
516
|
|
|
User::$info->groups = array_merge(User::$info->groups, [3]); |
517
|
1 |
|
} |
518
|
|
|
} |
519
|
1 |
|
|
520
|
|
|
/** |
521
|
1 |
|
* Load this user's permissions. |
522
|
|
|
* |
523
|
|
|
* What it does: |
524
|
|
|
* |
525
|
|
|
* - If the user is an admin, validate that they have not been banned. |
526
|
|
|
* - Attempt to load permissions from cache for cache level > 2 |
527
|
|
|
* - See if the user is possibly a robot and apply added permissions as needed |
528
|
1 |
|
* - Load permissions from the general permissions table. |
529
|
|
|
* - If inside a board load the necessary board permissions. |
530
|
1 |
|
* - If the user is not a guest, identify what other boards they have access to. |
531
|
|
|
* |
532
|
1 |
|
* @throws \ElkArte\Exceptions\Exception |
533
|
|
|
*/ |
534
|
|
|
function loadPermissions() |
535
|
|
|
{ |
536
|
|
|
global $board, $board_info, $modSettings; |
537
|
|
|
|
538
|
|
|
$db = database(); |
539
|
|
|
|
540
|
|
|
if (User::$info->is_admin) |
541
|
|
|
{ |
542
|
|
|
banPermissions(); |
543
|
|
|
|
544
|
|
|
return; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
$permissions = []; |
548
|
|
|
$removals = []; |
549
|
|
|
|
550
|
|
|
$cache = Cache::instance(); |
551
|
|
|
if ($cache->isEnabled()) |
552
|
|
|
{ |
553
|
|
|
$cache_groups = User::$info->groups; |
554
|
|
|
asort($cache_groups); |
555
|
|
|
$cache_groups = implode(',', $cache_groups); |
556
|
|
|
|
557
|
|
|
// If it's a spider then cache it different. |
558
|
|
|
if (User::$info->possibly_robot) |
559
|
|
|
{ |
560
|
|
|
$cache_groups .= '-spider'; |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
$cache_key = 'permissions:' . $cache_groups; |
564
|
1 |
|
$cache_board_key = 'permissions:' . $cache_groups . ':' . $board; |
565
|
|
|
|
566
|
1 |
|
if ($cache->levelHigherThan(1) && !empty($board) && $cache->getVar($temp, $cache_board_key, 240) && time() - 240 > $modSettings['settings_updated']) |
567
|
|
|
{ |
568
|
|
|
[User::$info->permissions] = $temp; |
569
|
1 |
|
banPermissions(); |
570
|
|
|
|
571
|
|
|
return; |
572
|
|
|
} |
573
|
|
|
|
574
|
1 |
|
if ($cache->getVar($temp, $cache_key, 240) && time() - 240 > $modSettings['settings_updated'] && is_array($temp)) |
575
|
|
|
{ |
576
|
1 |
|
[$permissions, $removals] = $temp; |
577
|
1 |
|
} |
578
|
|
|
} |
579
|
|
|
|
580
|
1 |
|
// If it is detected as a robot, and we are restricting permissions as a special group - then implement this. |
581
|
1 |
|
$spider_restrict = User::$info->possibly_robot && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : ''; |
582
|
|
|
|
583
|
1 |
|
if (empty($permissions)) |
584
|
|
|
{ |
585
|
|
|
// Get the general permissions. |
586
|
|
|
$db->fetchQuery(' |
587
|
|
|
SELECT |
588
|
|
|
permission, add_deny |
589
|
1 |
|
FROM {db_prefix}permissions |
590
|
|
|
WHERE id_group IN ({array_int:member_groups}) |
591
|
|
|
' . $spider_restrict, |
592
|
1 |
|
[ |
593
|
1 |
|
'member_groups' => User::$info->groups, |
594
|
|
|
'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0, |
595
|
1 |
|
] |
596
|
|
|
)->fetch_callback( |
597
|
|
|
static function ($row) use (&$removals, &$permissions) { |
598
|
|
|
if (empty($row['add_deny'])) |
599
|
|
|
{ |
600
|
|
|
$removals[] = $row['permission']; |
601
|
|
|
} |
602
|
1 |
|
else |
603
|
|
|
{ |
604
|
|
|
$permissions[] = $row['permission']; |
605
|
|
|
} |
606
|
|
|
} |
607
|
|
|
); |
608
|
|
|
|
609
|
|
|
if (isset($cache_key)) |
610
|
|
|
{ |
611
|
|
|
$cache->put($cache_key, [$permissions, $removals], 240); |
612
|
|
|
} |
613
|
|
|
} |
614
|
|
|
|
615
|
|
|
// Get the board permissions. |
616
|
|
|
if (!empty($board)) |
617
|
|
|
{ |
618
|
|
|
// Make sure the board (if any) has been loaded by loadBoard(). |
619
|
|
|
if (!isset($board_info['profile'])) |
620
|
|
|
{ |
621
|
|
|
throw new \ElkArte\Exceptions\Exception('no_board'); |
622
|
|
|
} |
623
|
|
|
|
624
|
|
|
$db->fetchQuery(' |
625
|
|
|
SELECT |
626
|
|
|
permission, add_deny |
627
|
|
|
FROM {db_prefix}board_permissions |
628
|
|
|
WHERE (id_group IN ({array_int:member_groups}) |
629
|
|
|
' . $spider_restrict . ') |
630
|
|
|
AND id_profile = {int:id_profile}', |
631
|
|
|
array( |
632
|
|
|
'member_groups' => User::$info->groups, |
633
|
|
|
'id_profile' => $board_info['profile'], |
634
|
|
|
'spider_group' => !empty($modSettings['spider_group']) && $modSettings['spider_group'] != 1 ? $modSettings['spider_group'] : 0, |
635
|
|
|
) |
636
|
|
|
)->fetch_callback( |
637
|
|
|
static function ($row) use (&$removals, &$permissions) { |
638
|
|
|
if (empty($row['add_deny'])) |
639
|
|
|
{ |
640
|
1 |
|
$removals[] = $row['permission']; |
641
|
|
|
} |
642
|
|
|
else |
643
|
|
|
{ |
644
|
|
|
$permissions[] = $row['permission']; |
645
|
1 |
|
} |
646
|
|
|
} |
647
|
|
|
); |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
User::$info->permissions = $permissions; |
651
|
1 |
|
|
652
|
|
|
// Remove all the permissions they shouldn't have ;). |
653
|
|
|
if (!empty($modSettings['permission_enable_deny'])) |
654
|
1 |
|
{ |
655
|
|
|
User::$info->permissions = array_diff(User::$info->permissions, $removals); |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
if (isset($cache_board_key) && !empty($board) && $cache->levelHigherThan(1)) |
659
|
|
|
{ |
660
|
|
|
$cache->put($cache_board_key, [User::$info->permissions, null], 240); |
661
|
|
|
} |
662
|
|
|
|
663
|
|
|
// Banned? Watch, don't touch.. |
664
|
|
|
banPermissions(); |
665
|
|
|
|
666
|
|
|
// Load the mod cache, so we can know what additional boards they should see, but no sense in doing it for guests |
667
|
1 |
|
if (User::$info->is_guest === false) |
668
|
|
|
{ |
669
|
|
|
User::$info->is_moderator = User::$info->is_mod || allowedTo('moderate_board'); |
670
|
|
|
if (!isset($_SESSION['mc']) || $_SESSION['mc']['time'] <= $modSettings['settings_updated']) |
671
|
|
|
{ |
672
|
|
|
require_once(SUBSDIR . '/Auth.subs.php'); |
673
|
|
|
rebuildModCache(); |
674
|
|
|
} |
675
|
|
|
else |
676
|
229 |
|
{ |
677
|
|
|
User::$info->mod_cache = $_SESSION['mc']; |
678
|
|
|
} |
679
|
229 |
|
} |
680
|
229 |
|
} |
681
|
229 |
|
|
682
|
229 |
|
/** |
683
|
|
|
* Load a theme, by ID. |
684
|
|
|
* |
685
|
|
|
* What it does: |
686
|
|
|
* |
687
|
|
|
* - identify the theme to be loaded. |
688
|
|
|
* - validate that the theme is valid and that the user has permission to use it |
689
|
|
|
* - load the users theme settings and site settings into $options. |
690
|
|
|
* - prepares the list of folders to search for template loading. |
691
|
|
|
* - sets up $context['user'] |
692
|
|
|
* - detects the users browser and sets a mobile friendly environment if needed |
693
|
|
|
* - loads default JS variables for use in every theme |
694
|
|
|
* - loads default JS scripts for use in every theme |
695
|
|
|
* |
696
|
|
|
* @param int $id_theme = 0 |
697
|
|
|
* @param bool $initialize = true |
698
|
|
|
* @deprecated since 2.0; use the theme object |
699
|
|
|
* |
700
|
|
|
*/ |
701
|
|
|
function loadTheme($id_theme = 0, $initialize = true) |
702
|
|
|
{ |
703
|
|
|
Errors::instance()->log_deprecated('loadTheme()', \ElkArte\Themes\ThemeLoader::class); |
704
|
|
|
new ThemeLoader($id_theme, $initialize); |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
/** |
708
|
|
|
* Loads basic user information in to $context['user'] |
709
|
|
|
*/ |
710
|
|
|
function loadUserContext() |
711
|
|
|
{ |
712
|
|
|
global $context, $txt, $modSettings; |
713
|
|
|
|
714
|
|
|
// Set up the contextual user array. |
715
|
229 |
|
$context['user'] = [ |
716
|
|
|
'id' => (int) User::$info->id, |
717
|
|
|
'is_logged' => User::$info->is_guest === false, |
718
|
229 |
|
'is_guest' => (bool) User::$info->is_guest, |
719
|
229 |
|
'is_admin' => (bool) User::$info->is_admin, |
720
|
229 |
|
'is_mod' => (bool) User::$info->is_mod, |
721
|
229 |
|
'is_moderator' => (bool) User::$info->is_moderator, |
722
|
229 |
|
// A user can mod if they have permission to see the mod center, or they are a board/group/approval moderator. |
723
|
229 |
|
'can_mod' => (bool) User::$info->canMod($modSettings['postmod_active']), |
724
|
229 |
|
'username' => User::$info->username, |
725
|
|
|
'language' => User::$info->language, |
726
|
229 |
|
'email' => User::$info->email, |
727
|
229 |
|
'ignoreusers' => User::$info->ignoreusers, |
728
|
229 |
|
]; |
729
|
229 |
|
|
730
|
229 |
|
// @todo Base language is being loaded to late, placed here temporarily |
731
|
|
|
Txt::load('index+Addons', true, true); |
732
|
|
|
|
733
|
|
|
// Something for the guests |
734
|
229 |
|
if (!$context['user']['is_guest']) |
735
|
|
|
{ |
736
|
82 |
|
$context['user']['name'] = User::$info->name; |
737
|
|
|
} |
738
|
147 |
|
elseif (!empty($txt['guest_title'])) |
739
|
|
|
{ |
740
|
146 |
|
$context['user']['name'] = $txt['guest_title']; |
741
|
|
|
} |
742
|
|
|
|
743
|
229 |
|
loadSmileyEmojiData(); |
744
|
229 |
|
} |
745
|
229 |
|
|
746
|
229 |
|
/** |
747
|
|
|
* Sets path, set, type and enabled status for smile and emoji |
748
|
|
|
*/ |
749
|
|
|
function loadSmileyEmojiData() |
750
|
|
|
{ |
751
|
|
|
global $context, $modSettings, $options, $settings; |
752
|
|
|
|
753
|
|
|
// Using the theme specific or global set |
754
|
|
|
$context['smiley_set'] = empty($settings['smiley_sets_default']) ? $modSettings['smiley_sets_default'] : $settings['smiley_sets_default']; |
755
|
|
|
$context['emoji_set'] = $modSettings['emoji_selection'] ?? 'no-emoji'; |
756
|
|
|
|
757
|
|
|
// Where are current smiley and emoji sets are located |
758
|
231 |
|
$context['smiley_path'] = $modSettings['smileys_url'] . '/' . $context['smiley_set'] . '/'; |
759
|
|
|
$context['smiley_dir'] = $modSettings['smileys_dir'] . '/' . $context['smiley_set'] . '/'; |
760
|
231 |
|
$context['emoji_path'] = $modSettings['smileys_url'] . '/' . $context['emoji_set'] . '/'; |
761
|
|
|
|
762
|
231 |
|
// Normally set, but after an upgrade can be missing |
763
|
|
|
if (!isset($modSettings['smiley_sets_extensions'])) |
764
|
|
|
{ |
765
|
|
|
require_once(SUBSDIR . '/Smileys.subs.php'); |
766
|
|
|
$modSettings['smiley_sets_extensions'] = setSmileyExtensionArray(); |
767
|
|
|
} |
768
|
|
|
|
769
|
231 |
|
// And what type of smiley library is this, gif, png, etc. |
770
|
|
|
$smiley_sets_extensions = explode(',', $modSettings['smiley_sets_extensions']); |
771
|
|
|
$set_paths = explode(',', $modSettings['smiley_sets_known']); |
772
|
|
|
$context['smiley_extension'] = $smiley_sets_extensions[array_search($context['smiley_set'], $set_paths, true)]; |
773
|
|
|
$context['smiley_extension'] = $context['smiley_extension'] ?? 'svg'; |
774
|
|
|
|
775
|
|
|
// Do they even want to see smileys |
776
|
|
|
$context['smiley_enabled'] = empty($options['show_no_smileys']) && $context['smiley_set'] !== 'none'; |
777
|
|
|
$context['emoji_enabled'] = empty($options['show_no_smileys']) && $context['emoji_set'] !== 'no-emoji'; |
778
|
|
|
} |
779
|
|
|
|
780
|
|
|
/** |
781
|
|
|
* This loads the bare minimum data. |
782
|
|
|
* |
783
|
|
|
* @deprecated since 2.0; use the theme object |
784
|
|
|
* |
785
|
|
|
* - Needed by scheduled tasks, |
786
|
|
|
* - Needed by any other code that needs language files before the forum (the theme) is loaded. |
787
|
|
|
*/ |
788
|
|
|
function loadEssentialThemeData() |
789
|
|
|
{ |
790
|
|
|
Errors::instance()->log_deprecated('loadEssentialThemeData()', '\ElkArte\Themes\ThemeLoader::loadEssentialThemeData()'); |
791
|
|
|
|
792
|
|
|
ThemeLoader::loadEssentialThemeData(); |
793
|
|
|
} |
794
|
|
|
|
795
|
|
|
/** |
796
|
|
|
* Load a template - if the theme doesn't include it, use the default. |
797
|
|
|
* |
798
|
|
|
* What it does: |
799
|
|
|
* |
800
|
|
|
* - loads a template file with the name template_name from the current, default, or base theme. |
801
|
|
|
* - detects a wrong default theme directory and tries to work around it. |
802
|
|
|
* - can be used to only load style sheets by using false as the template name |
803
|
|
|
* loading of style sheets with this function is deprecated, use loadCSSFile instead |
804
|
|
|
* - if $settings['template_dirs'] is empty, it delays the loading of the template |
805
|
|
|
* |
806
|
|
|
* @param string|false $template_name |
807
|
|
|
* @param string[]|string $style_sheets any style sheets to load with the template |
808
|
|
|
* @param bool $fatal = true if fatal is true, dies with an error message if the template cannot be found |
809
|
|
|
* |
810
|
|
|
* @return bool|null |
811
|
|
|
* @deprecated since 2.0; use the theme object |
812
|
|
|
* |
813
|
|
|
* @uses the requireTemplate() function to actually load the file. |
814
|
|
|
*/ |
815
|
|
|
function loadTemplate($template_name, $style_sheets = array(), $fatal = true) |
816
|
|
|
{ |
817
|
|
|
Errors::instance()->log_deprecated('loadTemplate()', 'theme()->getTemplates()->load()'); |
818
|
|
|
|
819
|
|
|
return theme()->getTemplates()->load($template_name, $style_sheets, $fatal); |
|
|
|
|
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
/** |
823
|
|
|
* Load a sub-template. |
824
|
|
|
* |
825
|
|
|
* What it does: |
826
|
|
|
* |
827
|
|
|
* - loads the sub template specified by sub_template_name, which must be in an already-loaded template. |
828
|
|
|
* - if ?debug is in the query string, shows administrators a marker after every sub template |
829
|
|
|
* for debugging purposes. |
830
|
|
|
* |
831
|
|
|
* @param string $sub_template_name |
832
|
|
|
* @param bool|string $fatal = false |
833
|
|
|
* - $fatal = true is for templates that shouldn't get a 'pretty' error screen |
834
|
|
|
* - $fatal = 'ignore' to skip |
835
|
|
|
* |
836
|
|
|
* @return bool |
837
|
|
|
* @deprecated since 2.0; use the theme object |
838
|
|
|
*/ |
839
|
|
|
function loadSubTemplate($sub_template_name, $fatal = false) |
840
|
|
|
{ |
841
|
|
|
Errors::instance()->log_deprecated('loadSubTemplate()', 'theme()->getTemplates()->loadSubTemplate()'); |
842
|
|
|
theme()->getTemplates()->loadSubTemplate($sub_template_name, $fatal); |
843
|
|
|
|
844
|
|
|
return true; |
845
|
|
|
} |
846
|
|
|
|
847
|
|
|
/** |
848
|
|
|
* Add a CSS file for output later |
849
|
|
|
* |
850
|
|
|
* @param string[]|string $filenames string or array of filenames to work on |
851
|
|
|
* @param array $params = array() |
852
|
|
|
* Keys are the following: |
853
|
|
|
* - ['local'] (true/false): define if the file is local |
854
|
|
|
* - ['fallback'] (true/false): if false will attempt to load the file |
855
|
|
|
* from the default theme if not found in the current theme |
856
|
229 |
|
* - ['stale'] (true/false/string): if true or null, use cache stale, |
857
|
|
|
* false do not, or used a supplied string |
858
|
229 |
|
* @param string $id optional id to use in html id="" |
859
|
|
|
*/ |
860
|
|
|
function loadCSSFile($filenames, $params = array(), $id = '') |
861
|
|
|
{ |
862
|
|
|
global $context; |
863
|
229 |
|
|
864
|
|
|
if (empty($filenames)) |
865
|
229 |
|
{ |
866
|
|
|
return; |
867
|
|
|
} |
868
|
229 |
|
|
869
|
|
|
if (!is_array($filenames)) |
870
|
12 |
|
{ |
871
|
|
|
$filenames = [$filenames]; |
872
|
|
|
} |
873
|
229 |
|
|
874
|
229 |
|
if (in_array('admin.css', $filenames)) |
875
|
229 |
|
{ |
876
|
229 |
|
$filenames[] = $context['theme_variant'] . '/admin' . $context['theme_variant'] . '.css'; |
877
|
|
|
} |
878
|
229 |
|
|
879
|
229 |
|
$params['subdir'] = $params['subdir'] ?? 'css'; |
880
|
|
|
$params['extension'] = 'css'; |
881
|
|
|
$params['index_name'] = 'css_files'; |
882
|
|
|
$params['debug_index'] = 'sheets'; |
883
|
|
|
|
884
|
|
|
loadAssetFile($filenames, $params, $id); |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
/** |
888
|
|
|
* Add a Javascript file for output later |
889
|
|
|
* |
890
|
|
|
* What it does: |
891
|
|
|
* |
892
|
|
|
* - Can be passed an array of filenames, all which will have the same |
893
|
|
|
* parameters applied, |
894
|
|
|
* - if you need specific parameters on a per file basis, call it multiple times |
895
|
|
|
* |
896
|
|
|
* @param string[]|string $filenames string or array of filenames to work on |
897
|
|
|
* @param array $params = array() |
898
|
|
|
* Keys are the following: |
899
|
|
|
* - ['local'] (true/false): define if the file is local, if file does not |
900
|
|
|
* start with http its assumed local |
901
|
|
|
* - ['defer'] (true/false): define if the file should load in <head> with the |
902
|
|
|
* defer attribute (script is fetched asynchronously) and run after page is loaded |
903
|
|
|
* - ['fallback'] (true/false): if true will attempt to load the file from the |
904
|
|
|
* default theme if not found in the current this is the default behavior |
905
|
|
|
* if this is not supplied |
906
|
|
|
* - ['async'] (true/false): if the script should be loaded asynchronously and |
907
|
231 |
|
* as soon as its loaded, interrupt parsing to run |
908
|
|
|
* - ['stale'] (true/false/string): if true or null, use cache stale, false do |
909
|
|
|
* not, or used a supplied string |
910
|
|
|
* @param string $id = '' optional id to use in html id="" |
911
|
|
|
*/ |
912
|
231 |
|
function loadJavascriptFile($filenames, $params = array(), $id = '') |
913
|
231 |
|
{ |
914
|
231 |
|
if (empty($filenames)) |
915
|
231 |
|
{ |
916
|
|
|
return; |
917
|
231 |
|
} |
918
|
231 |
|
|
919
|
|
|
$params['subdir'] = $params['subdir'] ?? 'scripts'; |
920
|
|
|
$params['extension'] = 'js'; |
921
|
|
|
$params['index_name'] = 'js_files'; |
922
|
|
|
$params['debug_index'] = 'javascript'; |
923
|
|
|
|
924
|
|
|
loadAssetFile($filenames, $params, $id); |
925
|
|
|
} |
926
|
|
|
|
927
|
|
|
/** |
928
|
|
|
* Add an asset (css, js or other) file for output later |
929
|
|
|
* |
930
|
|
|
* What it does: |
931
|
|
|
* |
932
|
|
|
* - Can be passed an array of filenames, all which will have the same |
933
|
|
|
* parameters applied, |
934
|
|
|
* - If you need specific parameters on a per file basis, call it multiple times |
935
|
|
|
* |
936
|
|
|
* @param string[]|string $filenames string or array of filenames to work on |
937
|
|
|
* @param array $params = array() |
938
|
|
|
* Keys are the following: |
939
|
|
|
* - ['subdir'] (string): the subdirectory of the theme dir the file is in |
940
|
|
|
* - ['extension'] (string): the extension of the file (e.g. css) |
941
|
|
|
* - ['index_name'] (string): the $context index that holds the array of loaded |
942
|
|
|
* files |
943
|
|
|
* - ['debug_index'] (string): the index that holds the array of loaded |
944
|
|
|
* files for debugging debug |
945
|
|
|
* - ['local'] (true/false): define if the file is local, if file does not |
946
|
|
|
* start with http or // (schema-less URLs) its assumed local. |
947
|
|
|
* The parameter is in fact useful only for files whose name starts with |
948
|
|
|
* http, in any other case (e.g. passing a local URL) the parameter would |
949
|
|
|
* fail in properly adding the file to the list. |
950
|
|
|
* - ['defer'] (true/false): define if the file should load in <head> or before |
951
|
|
|
* the closing <html> tag |
952
|
|
|
* - ['fallback'] (true/false): if true will attempt to load the file from the |
953
|
|
|
* default theme if not found in the current this is the default behavior |
954
|
|
|
* if this is not supplied |
955
|
231 |
|
* - ['async'] (true/false): if the script should be loaded asynchronously (HTML5) |
956
|
|
|
* - ['stale'] (true/false/string): if true or null, use cache stale, false do |
957
|
231 |
|
* not, or used a supplied string |
958
|
|
|
* @param string $id = '' optional id to use in html id="" |
959
|
|
|
*/ |
960
|
|
|
function loadAssetFile($filenames, $params = array(), $id = '') |
961
|
|
|
{ |
962
|
231 |
|
global $settings, $context, $db_show_debug; |
963
|
|
|
|
964
|
231 |
|
if (empty($filenames)) |
965
|
|
|
{ |
966
|
161 |
|
return; |
967
|
|
|
} |
968
|
|
|
|
969
|
|
|
$cache = Cache::instance(); |
970
|
231 |
|
$fileFunc = FileFunctions::instance(); |
971
|
|
|
|
972
|
231 |
|
if (!is_array($filenames)) |
973
|
|
|
{ |
974
|
|
|
$filenames = array($filenames); |
975
|
|
|
} |
976
|
|
|
|
977
|
|
|
// Static values for all these settings |
978
|
|
|
$staler_string = ''; |
979
|
|
|
if (!isset($params['stale']) || $params['stale'] === true) |
980
|
|
|
{ |
981
|
|
|
$staler_string = CACHE_STALE; |
982
|
|
|
} |
983
|
231 |
|
elseif (is_string($params['stale'])) |
984
|
231 |
|
{ |
985
|
|
|
$staler_string = ($params['stale'][0] === '?' ? $params['stale'] : '?' . $params['stale']); |
986
|
|
|
} |
987
|
231 |
|
|
988
|
231 |
|
$fallback = !isset($params['fallback']) || $params['fallback'] !== false; |
989
|
231 |
|
$dir = '/' . $params['subdir'] . '/'; |
990
|
|
|
|
991
|
|
|
// Whoa ... we've done this before yes? |
992
|
|
|
$cache_name = 'load_' . $params['extension'] . '_' . hash('md5', $settings['theme_dir'] . implode('_', $filenames)); |
993
|
|
|
$temp = []; |
994
|
|
|
if ($cache->getVar($temp, $cache_name, 600)) |
995
|
|
|
{ |
996
|
|
|
if (empty($context[$params['index_name']])) |
997
|
|
|
{ |
998
|
|
|
$context[$params['index_name']] = []; |
999
|
|
|
} |
1000
|
|
|
|
1001
|
|
|
$context[$params['index_name']] += $temp; |
1002
|
|
|
|
1003
|
|
|
if ($db_show_debug === true) |
1004
|
|
|
{ |
1005
|
|
|
foreach ($temp as $temp_params) |
1006
|
|
|
{ |
1007
|
|
|
Debug::instance()->add($params['debug_index'], $temp_params['options']['basename'] . '(' . (empty($temp_params['options']['local']) ? '' : (empty($temp_params['options']['url']) ? basename($temp_params['options']['dir']) : basename($temp_params['options']['url']))) . ')'); |
1008
|
231 |
|
} |
1009
|
|
|
} |
1010
|
|
|
} |
1011
|
231 |
|
else |
1012
|
|
|
{ |
1013
|
|
|
$this_build = []; |
1014
|
231 |
|
|
1015
|
231 |
|
// All the files in this group use the above parameters |
1016
|
|
|
foreach ($filenames as $filename) |
1017
|
|
|
{ |
1018
|
|
|
// Account for shorthand like admin.ext?xyz11 filenames |
1019
|
|
|
$has_cache_staler = strpos($filename, '.' . $params['extension'] . '?'); |
1020
|
|
|
$cache_staler = $staler_string; |
1021
|
|
|
if ($has_cache_staler) |
1022
|
231 |
|
{ |
1023
|
231 |
|
$params['basename'] = substr($filename, 0, $has_cache_staler + strlen($params['extension']) + 1); |
1024
|
|
|
} |
1025
|
231 |
|
else |
1026
|
|
|
{ |
1027
|
|
|
$params['basename'] = $filename; |
1028
|
231 |
|
} |
1029
|
|
|
|
1030
|
231 |
|
$this_id = empty($id) ? str_replace('?', '_', basename($filename)) : $id; |
1031
|
231 |
|
|
1032
|
231 |
|
// Is this a local file? |
1033
|
|
|
if (!empty($params['local']) || (strpos($filename, 'http') !== 0 && strpos($filename, '//') !== 0)) |
1034
|
|
|
{ |
1035
|
231 |
|
$params['local'] = true; |
1036
|
|
|
$params['dir'] = $settings['theme_dir'] . $dir; |
1037
|
|
|
$params['url'] = $settings['theme_url']; |
1038
|
|
|
|
1039
|
|
|
// Fallback if we are not already in the default theme |
1040
|
|
|
if ($fallback && ($settings['theme_dir'] !== $settings['default_theme_dir']) && !$fileFunc->fileExists($settings['theme_dir'] . $dir . $params['basename'])) |
1041
|
|
|
{ |
1042
|
|
|
// Can't find it in this theme, how about the default? |
1043
|
|
|
$filename = false; |
1044
|
|
|
if ($fileFunc->fileExists($settings['default_theme_dir'] . $dir . $params['basename'])) |
1045
|
|
|
{ |
1046
|
|
|
$filename = $settings['default_theme_url'] . $dir . $params['basename'] . $cache_staler; |
1047
|
|
|
$params['dir'] = $settings['default_theme_dir'] . $dir; |
1048
|
|
|
$params['url'] = $settings['default_theme_url']; |
1049
|
|
|
} |
1050
|
|
|
} |
1051
|
231 |
|
else |
1052
|
|
|
{ |
1053
|
|
|
$filename = $settings['theme_url'] . $dir . $params['basename'] . $cache_staler; |
1054
|
|
|
} |
1055
|
|
|
} |
1056
|
231 |
|
|
1057
|
|
|
// Add it to the array for use in the template |
1058
|
231 |
|
if (!empty($filename)) |
1059
|
|
|
{ |
1060
|
231 |
|
$this_build[$this_id] = array('filename' => $filename, 'options' => $params); |
1061
|
|
|
$context[$params['index_name']][$this_id] = $this_build[$this_id]; |
1062
|
|
|
|
1063
|
|
|
if ($db_show_debug === true) |
1064
|
|
|
{ |
1065
|
|
|
Debug::instance()->add($params['debug_index'], $params['basename'] . '(' . (empty($params['local']) ? '' : (empty($params['url']) ? basename($params['dir']) : basename($params['url']))) . ')'); |
1066
|
|
|
} |
1067
|
231 |
|
} |
1068
|
|
|
|
1069
|
|
|
// Save it, so we don't have to build this so often |
1070
|
231 |
|
$cache->put($cache_name, $this_build, 600); |
1071
|
|
|
} |
1072
|
|
|
} |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
/** |
1076
|
|
|
* Add a Javascript variable for output later (for feeding text strings and similar to JS) |
1077
|
|
|
* |
1078
|
|
|
* @param array $vars array of vars to include in the output done as 'varname' => 'var value' |
1079
|
|
|
* @param bool $escape = false, whether or not to escape the value |
1080
|
|
|
* @deprecated since 2.0; use the theme object |
1081
|
|
|
* |
1082
|
|
|
*/ |
1083
|
|
|
function addJavascriptVar($vars, $escape = false) |
1084
|
|
|
{ |
1085
|
|
|
Errors::instance()->log_deprecated('addJavascriptVar()', 'theme()->getTemplates()->addJavascriptVar()'); |
1086
|
|
|
theme()->addJavascriptVar($vars, $escape); |
1087
|
|
|
} |
1088
|
|
|
|
1089
|
|
|
/** |
1090
|
|
|
* Add a block of inline Javascript code to be executed later |
1091
|
|
|
* |
1092
|
|
|
* What it does: |
1093
|
|
|
* |
1094
|
|
|
* - only use this if you have to, generally external JS files are better, but for very small scripts |
1095
|
|
|
* or for scripts that require help from PHP/whatever, this can be useful. |
1096
|
|
|
* - all code added with this function is added to the same <script> tag so do make sure your JS is clean! |
1097
|
|
|
* |
1098
|
|
|
* @param string $javascript |
1099
|
|
|
* @param bool $defer = false, define if the script should load in <head> or before the closing <html> tag |
1100
|
|
|
* @deprecated since 2.0; use the theme object |
1101
|
|
|
* |
1102
|
|
|
*/ |
1103
|
|
|
function addInlineJavascript($javascript, $defer = false) |
1104
|
|
|
{ |
1105
|
|
|
Errors::instance()->log_deprecated('addInlineJavascript()', 'theme()->addInlineJavascript()'); |
1106
|
|
|
theme()->addInlineJavascript($javascript, $defer); |
1107
|
|
|
} |
1108
|
|
|
|
1109
|
|
|
/** |
1110
|
|
|
* Load a language file. |
1111
|
|
|
* |
1112
|
|
|
* - Tries the current and default themes as well as the user and global languages. |
1113
|
|
|
* |
1114
|
|
|
* @param string $template_name |
1115
|
|
|
* @param string $lang = '' |
1116
|
|
|
* @param bool $fatal = true |
1117
|
|
|
* @param bool $force_reload = false |
1118
|
|
|
* @deprecated since 2.0; use the theme object |
1119
|
|
|
* |
1120
|
|
|
*/ |
1121
|
|
|
function loadLanguage($template_name, $lang = '', $fatal = true, $force_reload = false) |
1122
|
|
|
{ |
1123
|
|
|
Txt::load($template_name, $lang, $fatal); |
1124
|
|
|
} |
1125
|
|
|
|
1126
|
|
|
/** |
1127
|
|
|
* Get all parent boards (requires first parent as parameter) |
1128
|
|
|
* |
1129
|
|
|
* What it does: |
1130
|
|
|
* |
1131
|
|
|
* - It finds all the parents of id_parent, and that board itself. |
1132
|
|
|
* - Additionally, it detects the moderators of said boards. |
1133
|
|
|
* - Returns an array of information about the boards found. |
1134
|
|
|
* |
1135
|
|
|
* @param int $id_parent |
1136
|
|
|
* |
1137
|
1 |
|
* @return array |
1138
|
|
|
* @throws \ElkArte\Exceptions\Exception parent_not_found |
1139
|
1 |
|
*/ |
1140
|
1 |
|
function getBoardParents($id_parent) |
1141
|
1 |
|
{ |
1142
|
1 |
|
$db = database(); |
1143
|
1 |
|
$cache = Cache::instance(); |
1144
|
1 |
|
$boards = array(); |
1145
|
1 |
|
|
1146
|
1 |
|
// First check if we have this cached already. |
1147
|
|
|
if (!$cache->getVar($boards, 'board_parents-' . $id_parent, 480)) |
1148
|
1 |
|
{ |
1149
|
1 |
|
$boards = array(); |
1150
|
1 |
|
$original_parent = $id_parent; |
1151
|
1 |
|
|
1152
|
1 |
|
// Loop while the parent is non-zero. |
1153
|
1 |
|
while ($id_parent != 0) |
1154
|
1 |
|
{ |
1155
|
1 |
|
$result = $db->query('', ' |
1156
|
|
|
SELECT |
1157
|
1 |
|
b.id_parent, b.name, {int:board_parent} AS id_board, COALESCE(mem.id_member, 0) AS id_moderator, |
1158
|
1 |
|
mem.real_name, b.child_level |
1159
|
1 |
|
FROM {db_prefix}boards AS b |
1160
|
1 |
|
LEFT JOIN {db_prefix}moderators AS mods ON (mods.id_board = b.id_board) |
1161
|
1 |
|
LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = mods.id_member) |
1162
|
1 |
|
WHERE b.id_board = {int:board_parent}', |
1163
|
1 |
|
array( |
1164
|
1 |
|
'board_parent' => $id_parent, |
1165
|
1 |
|
) |
1166
|
1 |
|
); |
1167
|
1 |
|
// In the EXTREMELY unlikely event this happens, give an error message. |
1168
|
1 |
|
if ($result->num_rows() === 0) |
1169
|
1 |
|
{ |
1170
|
|
|
throw new \ElkArte\Exceptions\Exception('parent_not_found', 'critical'); |
1171
|
1 |
|
} |
1172
|
1 |
|
|
1173
|
1 |
|
while (($row = $result->fetch_assoc())) |
1174
|
1 |
|
{ |
1175
|
1 |
|
if (!isset($boards[$row['id_board']])) |
1176
|
1 |
|
{ |
1177
|
1 |
|
$id_parent = $row['id_parent']; |
1178
|
1 |
|
$boards[$row['id_board']] = array( |
1179
|
1 |
|
'url' => getUrl('board', ['board' => $row['id_board'], 'name' => $row['name'], 'start' => '0']), |
1180
|
1 |
|
'name' => $row['name'], |
1181
|
1 |
|
'level' => $row['child_level'], |
1182
|
1 |
|
'moderators' => array() |
1183
|
1 |
|
); |
1184
|
|
|
} |
1185
|
1 |
|
|
1186
|
1 |
|
// If a moderator exists for this board, add that moderator for all children too. |
1187
|
1 |
|
if (!empty($row['id_moderator'])) |
1188
|
1 |
|
{ |
1189
|
1 |
|
foreach (array_keys($boards) as $id) |
1190
|
1 |
|
{ |
1191
|
1 |
|
$boards[$id]['moderators'][$row['id_moderator']] = array( |
1192
|
1 |
|
'id' => $row['id_moderator'], |
1193
|
1 |
|
'name' => $row['real_name'], |
1194
|
1 |
|
'href' => getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]), |
1195
|
1 |
|
'link' => '<a href="' . getUrl('profile', ['action' => 'profile', 'u' => $row['id_moderator']]) . '">' . $row['real_name'] . '</a>' |
1196
|
1 |
|
); |
1197
|
1 |
|
} |
1198
|
|
|
} |
1199
|
1 |
|
} |
1200
|
|
|
|
1201
|
|
|
$result->free_result(); |
1202
|
|
|
} |
1203
|
|
|
|
1204
|
|
|
$cache->put('board_parents-' . $original_parent, $boards, 480); |
1205
|
|
|
} |
1206
|
|
|
|
1207
|
|
|
return $boards; |
1208
|
|
|
} |
1209
|
|
|
|
1210
|
|
|
/** |
1211
|
|
|
* Attempt to reload our known languages. |
1212
|
|
|
* |
1213
|
|
|
* @param bool $use_cache = true |
1214
|
|
|
* |
1215
|
|
|
* @return array |
1216
|
|
|
*/ |
1217
|
34 |
|
function getLanguages($use_cache = true) |
1218
|
|
|
{ |
1219
|
34 |
|
$cache = Cache::instance(); |
1220
|
34 |
|
|
1221
|
34 |
|
// Either we don't use the cache, or its expired. |
1222
|
|
|
$languages = []; |
1223
|
|
|
$language_dir = SOURCEDIR . '/ElkArte/Languages/Index'; |
1224
|
34 |
|
|
1225
|
|
|
if (!$use_cache || !$cache->getVar($languages, 'known_languages', $cache->levelLowerThan(2) ? 86400 : 3600)) |
1226
|
34 |
|
{ |
1227
|
34 |
|
$dir = dir($language_dir . '/'); |
1228
|
|
|
while (($entry = $dir->read())) |
1229
|
|
|
{ |
1230
|
34 |
|
if ($entry === '.' || $entry === '..') |
1231
|
|
|
{ |
1232
|
22 |
|
continue; |
1233
|
|
|
} |
1234
|
|
|
|
1235
|
|
|
$basename = basename($entry, '.php'); |
1236
|
|
|
$languages[$basename] = array( |
1237
|
|
|
'name' => $basename, |
1238
|
|
|
'selected' => false, |
1239
|
|
|
'filename' => $entry, |
1240
|
|
|
'location' => $language_dir . '/' . $entry, |
1241
|
22 |
|
); |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
|
|
$dir->close(); |
1245
|
22 |
|
|
1246
|
|
|
// Let's cash in on this deal. |
1247
|
|
|
$cache->put('known_languages', $languages, $cache->isEnabled() && $cache->levelLowerThan(1) ? 86400 : 3600); |
1248
|
|
|
} |
1249
|
22 |
|
|
1250
|
|
|
return $languages; |
1251
|
22 |
|
} |
1252
|
|
|
|
1253
|
22 |
|
/** |
1254
|
22 |
|
* Initialize a database connection. |
1255
|
22 |
|
*/ |
1256
|
22 |
|
function loadDatabase() |
1257
|
22 |
|
{ |
1258
|
|
|
global $db_prefix, $db_name; |
1259
|
|
|
|
1260
|
|
|
// Database stuffs |
1261
|
|
|
require_once(SOURCEDIR . '/database/Database.subs.php'); |
1262
|
|
|
|
1263
|
22 |
|
// Safeguard here, if there isn't a valid connection lets put a stop to it. |
1264
|
|
|
try |
1265
|
|
|
{ |
1266
|
|
|
$db = database(false); |
1267
|
|
|
} |
1268
|
|
|
catch (Exception) |
1269
|
|
|
{ |
1270
|
|
|
Errors::instance()->display_db_error(); |
1271
|
|
|
} |
1272
|
|
|
|
1273
|
|
|
// If in SSI mode fix up the prefix. |
1274
|
|
|
if (ELK === 'SSI') |
1275
|
|
|
{ |
1276
|
22 |
|
$db_prefix = $db->fix_prefix($db_prefix, $db_name); |
1277
|
|
|
} |
1278
|
|
|
// Case-sensitive database? Let's define a constant. |
1279
|
34 |
|
// @NOTE: I think it is already taken care by the abstraction, it should be possible to remove |
1280
|
|
|
if (!$db->case_sensitive()) |
1281
|
|
|
{ |
1282
|
34 |
|
return; |
1283
|
|
|
} |
1284
|
|
|
|
1285
|
|
|
if (defined('DB_CASE_SENSITIVE')) |
1286
|
|
|
{ |
1287
|
|
|
return; |
1288
|
|
|
} |
1289
|
|
|
|
1290
|
|
|
define('DB_CASE_SENSITIVE', '1'); |
1291
|
|
|
} |
1292
|
|
|
|
1293
|
|
|
/** |
1294
|
4 |
|
* Determine the user's avatar type and return the information as an array |
1295
|
|
|
* |
1296
|
4 |
|
* @param array $profile array containing the users profile data |
1297
|
|
|
* |
1298
|
|
|
* @return array $avatar |
1299
|
4 |
|
* |
1300
|
|
|
* @event integrate_avatar allows access to $avatar array before it is returned |
1301
|
4 |
|
*/ |
1302
|
|
|
function determineAvatar($profile) |
1303
|
|
|
{ |
1304
|
4 |
|
global $modSettings, $settings, $context; |
1305
|
|
|
|
1306
|
|
|
if (empty($profile)) |
1307
|
|
|
{ |
1308
|
|
|
return []; |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
4 |
|
$avatar_protocol = empty($profile['avatar']) ? '' : strtolower(substr($profile['avatar'], 0, 7)); |
1312
|
4 |
|
$alt = $profile['member_name'] ?? ''; |
1313
|
|
|
|
1314
|
|
|
// Build the gravatar request once. |
1315
|
|
|
$gravatar = '//www.gravatar.com/avatar/' . |
1316
|
4 |
|
hash('md5', strtolower($profile['email_address'] ?? '')) . |
1317
|
|
|
'?s=' . $modSettings['avatar_max_height'] . |
1318
|
|
|
(empty($modSettings['gravatar_rating']) |
1319
|
|
|
? ('') |
1320
|
|
|
: '&r=' . $modSettings['gravatar_rating']) . |
1321
|
|
|
((!empty($modSettings['gravatar_default']) && $modSettings['gravatar_default'] !== 'none') |
1322
|
4 |
|
? ('&d=' . $modSettings['gravatar_default']) |
1323
|
|
|
: ''); |
1324
|
4 |
|
|
1325
|
|
|
// uploaded avatar? |
1326
|
|
|
if ($profile['id_attach'] > 0 && empty($profile['avatar'])) |
1327
|
4 |
|
{ |
1328
|
|
|
// where are those pesky avatars? |
1329
|
|
|
$avatar_url = empty($profile['attachment_type']) ? getUrl('action', ['action' => 'dlattach', 'attach' => $profile['id_attach'], 'type' => 'avatar']) : $modSettings['custom_avatar_url'] . '/' . $profile['filename']; |
1330
|
|
|
|
1331
|
|
|
$avatar = [ |
1332
|
4 |
|
'name' => $profile['avatar'], |
1333
|
4 |
|
'image' => '<img class="avatar avatarresize" src="' . $avatar_url . '" alt="' . $alt . '" loading="lazy" />', |
1334
|
|
|
'href' => $avatar_url, |
1335
|
|
|
'url' => '', |
1336
|
4 |
|
]; |
1337
|
|
|
} |
1338
|
4 |
|
// remote avatar? |
1339
|
|
|
elseif ($avatar_protocol === 'http://' || $avatar_protocol === 'https:/') |
1340
|
|
|
{ |
1341
|
|
|
$avatar = [ |
1342
|
4 |
|
'name' => $profile['avatar'], |
1343
|
4 |
|
'image' => '<img class="avatar avatarresize" src="' . $profile['avatar'] . '" alt="' . $alt . '" loading="lazy" />', |
1344
|
|
|
'href' => $profile['avatar'], |
1345
|
|
|
'url' => $profile['avatar'], |
1346
|
4 |
|
]; |
1347
|
|
|
} |
1348
|
4 |
|
// Gravatar instead? |
1349
|
|
|
elseif (!empty($profile['avatar']) && $profile['avatar'] === 'gravatar') |
1350
|
|
|
{ |
1351
|
4 |
|
// Gravatars URL. |
1352
|
4 |
|
$gravatar_url = $gravatar; |
1353
|
|
|
$avatar = array( |
1354
|
4 |
|
'name' => $profile['avatar'], |
1355
|
4 |
|
'image' => '<img class="avatar avatarresize" src="' . $gravatar_url . '" alt="' . $alt . '" loading="lazy" />', |
1356
|
|
|
'href' => $gravatar_url, |
1357
|
|
|
'url' => $gravatar_url, |
1358
|
4 |
|
); |
1359
|
|
|
} |
1360
|
4 |
|
// an avatar from the gallery? |
1361
|
|
|
elseif (!empty($profile['avatar']) && ($avatar_protocol !== 'http://' && $avatar_protocol !== 'https:/')) |
1362
|
|
|
{ |
1363
|
|
|
$avatar = [ |
1364
|
4 |
|
'name' => $profile['avatar'], |
1365
|
|
|
'image' => '<img class="avatar avatarresize" src="' . $modSettings['avatar_url'] . '/' . $profile['avatar'] . '" alt="' . $alt . '" loading="lazy" />', |
1366
|
|
|
'href' => $modSettings['avatar_url'] . '/' . $profile['avatar'], |
1367
|
4 |
|
'url' => $modSettings['avatar_url'] . '/' . $profile['avatar'], |
1368
|
|
|
]; |
1369
|
|
|
} |
1370
|
|
|
// no custom avatar found yet, maybe a default avatar? |
1371
|
|
|
elseif (!empty($modSettings['avatar_default']) && empty($profile['avatar']) && empty($profile['filename'])) |
1372
|
|
|
{ |
1373
|
|
|
// $settings not initialized? We can't do anything further.. |
1374
|
|
|
if (!empty($settings)) |
1375
|
1 |
|
{ |
1376
|
|
|
if (!empty($modSettings['avatar_gravatar_enabled']) && !empty($modSettings['gravatar_as_default']) |
1377
|
|
|
&& $modSettings['gravatar_default'] !== 'none') |
1378
|
1 |
|
{ |
1379
|
|
|
$href = $gravatar; |
1380
|
|
|
} |
1381
|
|
|
else |
1382
|
|
|
{ |
1383
|
1 |
|
// Use the theme, or its variants, default image |
1384
|
|
|
$href = $settings['images_url'] . '/default_avatar.png'; |
1385
|
|
|
$href_var = $settings['actual_theme_dir'] . '/images/' . $context['theme_variant'] . '/default_avatar.png'; |
1386
|
|
|
|
1387
|
|
|
if (!empty($context['theme_variant']) |
1388
|
|
|
&& FileFunctions::instance()->fileExists($href_var)) |
1389
|
|
|
{ |
1390
|
|
|
$href = $settings['images_url'] . '/' . $context['theme_variant'] . '/default_avatar.png'; |
1391
|
1 |
|
} |
1392
|
|
|
} |
1393
|
|
|
|
1394
|
|
|
// Let's proceed with the default avatar. |
1395
|
|
|
// TODO: This should be incorporated into the theme. |
1396
|
|
|
$avatar = [ |
1397
|
|
|
'name' => '', |
1398
|
1 |
|
'image' => '<img class="avatar avatarresize" src="' . $href . '" alt="' . $alt . '" loading="lazy" />', |
1399
|
|
|
'href' => $href, |
1400
|
|
|
'url' => 'https://', |
1401
|
|
|
]; |
1402
|
1 |
|
} |
1403
|
|
|
else |
1404
|
|
|
{ |
1405
|
|
|
$avatar = []; |
1406
|
|
|
} |
1407
|
|
|
} |
1408
|
|
|
// finally ... |
1409
|
|
|
else |
1410
|
|
|
{ |
1411
|
|
|
$avatar = [ |
1412
|
|
|
'name' => '', |
1413
|
|
|
'image' => '', |
1414
|
|
|
'href' => '', |
1415
|
|
|
'url' => '' |
1416
|
13 |
|
]; |
1417
|
|
|
} |
1418
|
13 |
|
|
1419
|
|
|
// Make sure there's a preview for gravatars available. |
1420
|
|
|
$avatar['gravatar_preview'] = $gravatar; |
1421
|
|
|
|
1422
|
|
|
call_integration_hook('integrate_avatar', array(&$avatar, $profile)); |
1423
|
13 |
|
|
1424
|
|
|
return $avatar; |
1425
|
|
|
} |
1426
|
13 |
|
|
1427
|
|
|
/** |
1428
|
|
|
* Get information about the server |
1429
|
|
|
* |
1430
|
|
|
* @return Server |
1431
|
|
|
*/ |
1432
|
|
|
function detectServer() |
1433
|
|
|
{ |
1434
|
|
|
global $context; |
1435
|
|
|
static $server = null; |
1436
|
|
|
|
1437
|
|
|
if ($server === null) |
1438
|
|
|
{ |
1439
|
13 |
|
$server = new Server($_SERVER); |
1440
|
|
|
$servers = ['iis', 'apache', 'litespeed', 'lighttpd', 'nginx', 'cgi', 'windows']; |
1441
|
|
|
$context['server'] = array(); |
1442
|
|
|
foreach ($servers as $name) |
1443
|
|
|
{ |
1444
|
|
|
$context['server']['is_' . $name] = $server->is($name); |
1445
|
|
|
} |
1446
|
|
|
|
1447
|
|
|
$context['server']['iso_case_folding'] = $server->is('iso_case_folding'); |
1448
|
|
|
} |
1449
|
13 |
|
|
1450
|
|
|
return $server; |
1451
|
|
|
} |
1452
|
|
|
|
1453
|
|
|
/** |
1454
|
|
|
* Returns if a webserver is of type server (apache, nginx, etc) |
1455
|
|
|
* |
1456
|
|
|
* @param $server |
1457
|
|
|
* |
1458
|
|
|
* @return bool |
1459
|
|
|
*/ |
1460
|
|
|
function serverIs($server) |
1461
|
|
|
{ |
1462
|
13 |
|
return detectServer()->is($server); |
1463
|
|
|
} |
1464
|
|
|
|
1465
|
|
|
/** |
1466
|
|
|
* Do some important security checks: |
1467
|
|
|
* |
1468
|
|
|
* What it does: |
1469
|
|
|
* |
1470
|
|
|
* - Checks the existence of critical files e.g. install.php |
1471
|
|
|
* - Checks for an active admin session. |
1472
|
13 |
|
* - Checks cache directory is writable. |
1473
|
|
|
* - Calls secureDirectory to protect attachments & cache. |
1474
|
|
|
* - Checks if the forum is in maintenance mode. |
1475
|
|
|
*/ |
1476
|
|
|
function doSecurityChecks() |
1477
|
|
|
{ |
1478
|
|
|
global $modSettings, $context, $maintenance, $txt, $options; |
1479
|
|
|
|
1480
|
|
|
$show_warnings = false; |
1481
|
|
|
|
1482
|
|
|
$cache = Cache::instance(); |
1483
|
|
|
|
1484
|
|
|
if (User::$info->is_guest === false && allowedTo('admin_forum')) |
1485
|
|
|
{ |
1486
|
|
|
// If agreement is enabled, at least the english version shall exists |
1487
|
|
|
if ($modSettings['requireAgreement'] && !file_exists(SOURCEDIR . '/ElkArte/Languages/Agreement/English.txt')) |
1488
|
|
|
{ |
1489
|
|
|
$context['security_controls_files']['title'] = $txt['generic_warning']; |
1490
|
|
|
$context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing']; |
1491
|
|
|
$show_warnings = true; |
1492
|
|
|
} |
1493
|
|
|
|
1494
|
|
|
// Cache directory writable? |
1495
|
13 |
|
if ($cache->isEnabled() && !is_writable(CACHEDIR)) |
1496
|
|
|
{ |
1497
|
|
|
$context['security_controls_files']['title'] = $txt['generic_warning']; |
1498
|
|
|
$context['security_controls_files']['errors']['cache'] = $txt['cache_writable']; |
1499
|
|
|
$show_warnings = true; |
1500
|
|
|
} |
1501
|
|
|
|
1502
|
|
|
if (checkSecurityFiles()) |
1503
|
13 |
|
{ |
1504
|
|
|
$show_warnings = true; |
1505
|
13 |
|
} |
1506
|
|
|
|
1507
|
13 |
|
// We are already checking so many files...just few more doesn't make any difference! :P |
1508
|
|
|
$attachmentsDir = new AttachmentsDirectory($modSettings, database()); |
1509
|
|
|
$path = $attachmentsDir->getCurrent(); |
1510
|
|
|
secureDirectory($path, true); |
1511
|
|
|
secureDirectory(CACHEDIR, false, '"\.(js|css)$"'); |
1512
|
|
|
|
1513
|
|
|
// Active admin session? |
1514
|
|
|
if (isAdminSessionActive()) |
1515
|
288 |
|
{ |
1516
|
288 |
|
$context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'adminlogoff', 'redir', '{session_data}']))); |
1517
|
|
|
} |
1518
|
288 |
|
|
1519
|
|
|
// Maintenance mode enabled? |
1520
|
1 |
|
if (!empty($maintenance)) |
1521
|
1 |
|
{ |
1522
|
1 |
|
$context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], (getUrl('admin', ['action' => 'admin', 'area' => 'serversettings', '{session_data}']))); |
1523
|
1 |
|
} |
1524
|
|
|
|
1525
|
1 |
|
// New updates |
1526
|
|
|
if (defined('FORUM_VERSION')) |
1527
|
|
|
{ |
1528
|
1 |
|
$index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION); |
1529
|
|
|
if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index])) |
1530
|
1 |
|
{ |
1531
|
|
|
$show_warnings = true; |
1532
|
|
|
$context['new_version_updates'] = array( |
1533
|
288 |
|
'title' => $txt['new_version_updates'], |
1534
|
|
|
'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text'])), |
1535
|
|
|
); |
1536
|
|
|
} |
1537
|
|
|
} |
1538
|
|
|
} |
1539
|
|
|
|
1540
|
|
|
// Check for database errors. |
1541
|
|
|
if (!empty($_SESSION['query_command_denied'])) |
1542
|
|
|
{ |
1543
|
|
|
if (User::$info->is_admin) |
1544
|
|
|
{ |
1545
|
|
|
$context['security_controls_query']['title'] = $txt['query_command_denied']; |
1546
|
|
|
$show_warnings = true; |
1547
|
|
|
foreach ($_SESSION['query_command_denied'] as $command => $error) |
1548
|
|
|
{ |
1549
|
|
|
$context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>'; |
1550
|
|
|
} |
1551
|
|
|
} |
1552
|
|
|
else |
1553
|
|
|
{ |
1554
|
|
|
$context['security_controls_query']['title'] = $txt['query_command_denied_guests']; |
1555
|
|
|
foreach ($_SESSION['query_command_denied'] as $command => $error) |
1556
|
|
|
{ |
1557
|
|
|
$context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>'; |
1558
|
|
|
} |
1559
|
|
|
} |
1560
|
|
|
} |
1561
|
|
|
|
1562
|
|
|
// Are there any members waiting for approval? |
1563
|
|
|
if (allowedTo('moderate_forum') && ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers'])) |
1564
|
|
|
{ |
1565
|
|
|
$context['warning_controls']['unapproved_members'] = sprintf($txt[$modSettings['unapprovedMembers'] == 1 ? 'approve_one_member_waiting' : 'approve_many_members_waiting'], getUrl('admin', ['action' => 'admin', 'area' => 'viewmembers', 'sa' => 'browse', 'type' => 'approve']), $modSettings['unapprovedMembers']); |
1566
|
|
|
} |
1567
|
|
|
|
1568
|
|
|
if (!empty($context['open_mod_reports']) && (empty(User::$settings['mod_prefs']) || User::$settings['mod_prefs'][0] == 1)) |
1569
|
|
|
{ |
1570
|
|
|
$context['warning_controls']['open_mod_reports'] = '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'reports']) . '">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>'; |
1571
|
|
|
} |
1572
|
|
|
|
1573
|
|
|
if (!empty($context['open_pm_reports']) && allowedTo('admin_forum')) |
1574
|
|
|
{ |
1575
|
|
|
$context['warning_controls']['open_pm_reports'] = '<a href="' . getUrl('action', ['action' => 'moderate', 'area' => 'pm_reports']) . '">' . sprintf($txt['pm_reports_waiting'], $context['open_pm_reports']) . '</a>'; |
1576
|
|
|
} |
1577
|
|
|
|
1578
|
|
|
if (isset($_SESSION['ban']['cannot_post'])) |
1579
|
|
|
{ |
1580
|
|
|
// An admin cannot be banned (technically he could), and if it is better he knows. |
1581
|
|
|
$context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], User::$info->is_guest ? $txt['guest_title'] : User::$info->name); |
1582
|
|
|
$show_warnings = true; |
1583
|
|
|
|
1584
|
|
|
$context['security_controls_ban']['errors']['reason'] = ''; |
1585
|
|
|
|
1586
|
|
|
if (!empty($_SESSION['ban']['cannot_post']['reason'])) |
1587
|
|
|
{ |
1588
|
|
|
$context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason']; |
1589
|
|
|
} |
1590
|
|
|
|
1591
|
|
|
if (!empty($_SESSION['ban']['expire_time'])) |
1592
|
|
|
{ |
1593
|
|
|
$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>'; |
1594
|
|
|
} |
1595
|
|
|
else |
1596
|
|
|
{ |
1597
|
|
|
$context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>'; |
1598
|
|
|
} |
1599
|
|
|
} |
1600
|
|
|
|
1601
|
|
|
// Finally, let's show the layer. |
1602
|
|
|
if ($show_warnings || !empty($context['warning_controls'])) |
1603
|
|
|
{ |
1604
|
|
|
theme()->getLayers()->addAfter('admin_warning', 'body'); |
1605
|
|
|
} |
1606
|
|
|
} |
1607
|
|
|
|
1608
|
|
|
/** |
1609
|
|
|
* Load everything necessary for the BBC parsers |
1610
|
|
|
*/ |
1611
|
|
|
function loadBBCParsers() |
1612
|
|
|
{ |
1613
|
|
|
global $modSettings; |
1614
|
|
|
|
1615
|
|
|
// Set the default disabled BBC |
1616
|
|
|
if (!empty($modSettings['disabledBBC'])) |
1617
|
|
|
{ |
1618
|
|
|
if (!is_array($modSettings['disabledBBC'])) |
1619
|
|
|
{ |
1620
|
|
|
$disabledBBC = explode(',', $modSettings['disabledBBC']); |
1621
|
|
|
} |
1622
|
|
|
else |
1623
|
|
|
{ |
1624
|
|
|
$disabledBBC = $modSettings['disabledBBC']; |
1625
|
|
|
} |
1626
|
|
|
|
1627
|
|
|
ParserWrapper::instance()->setDisabled(empty($disabledBBC) ? array() : $disabledBBC); |
1628
|
|
|
} |
1629
|
|
|
|
1630
|
|
|
return 1; |
1631
|
|
|
} |
1632
|
|
|
|
1633
|
|
|
/** |
1634
|
|
|
* This is necessary to support data stored in the pre-1.0.8 way (i.e. serialized) |
1635
|
|
|
* |
1636
|
|
|
* @param string $variable The string to convert |
1637
|
|
|
* @param null|callable $save_callback The function that will save the data to the db |
1638
|
|
|
* @return array the array |
1639
|
|
|
*/ |
1640
|
|
|
function serializeToJson($variable, $save_callback = null) |
1641
|
|
|
{ |
1642
|
|
|
$array_form = json_decode($variable, true); |
1643
|
|
|
|
1644
|
|
|
// decoding failed, let's try with unserialize |
1645
|
|
|
if (!is_array($array_form)) |
1646
|
|
|
{ |
1647
|
|
|
try |
1648
|
|
|
{ |
1649
|
|
|
$array_form = Util::unserialize($variable); |
1650
|
|
|
} |
1651
|
|
|
catch (Exception) |
1652
|
|
|
{ |
1653
|
|
|
$array_form = false; |
1654
|
|
|
} |
1655
|
|
|
|
1656
|
|
|
// If unserialize fails as well, let's just store an empty array |
1657
|
|
|
if ($array_form === false) |
1658
|
|
|
{ |
1659
|
|
|
$array_form = array(0, '', 0); |
1660
|
|
|
} |
1661
|
|
|
|
1662
|
|
|
// Time to update the value if necessary |
1663
|
|
|
if ($save_callback !== null) |
1664
|
|
|
{ |
1665
|
|
|
$save_callback($array_form); |
1666
|
|
|
} |
1667
|
|
|
} |
1668
|
|
|
|
1669
|
|
|
return $array_form; |
1670
|
|
|
} |
1671
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.