1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This file concerns itself almost completely with theme administration. |
||
5 | * Its tasks include changing theme settings, installing and removing |
||
6 | * themes, choosing the current theme, and editing themes. |
||
7 | * |
||
8 | * @todo Update this for the new package manager? |
||
9 | * |
||
10 | * Creating and distributing theme packages: |
||
11 | * There isn't that much required to package and distribute your own themes... |
||
12 | * just do the following: |
||
13 | * - create a theme_info.xml file, with the root element theme-info. |
||
14 | * - its name should go in a name element, just like description. |
||
15 | * - your name should go in author. (email in the email attribute.) |
||
16 | * - any support website for the theme should be in website. |
||
17 | * - layers and templates (non-default) should go in those elements ;). |
||
18 | * - if the images dir isn't images, specify in the images element. |
||
19 | * - any extra rows for themes should go in extra, serialized. (as in array(variable => value).) |
||
20 | * - tar and gzip the directory - and you're done! |
||
21 | * - please include any special license in a license.txt file. |
||
22 | * |
||
23 | * Simple Machines Forum (SMF) |
||
24 | * |
||
25 | * @package SMF |
||
26 | * @author Simple Machines https://www.simplemachines.org |
||
27 | * @copyright 2022 Simple Machines and individual contributors |
||
28 | * @license https://www.simplemachines.org/about/smf/license.php BSD |
||
29 | * |
||
30 | * @version 2.1.0 |
||
31 | */ |
||
32 | |||
33 | if (!defined('SMF')) |
||
34 | die('No direct access...'); |
||
35 | |||
36 | /** |
||
37 | * Subaction handler - manages the action and delegates control to the proper |
||
38 | * sub-action. |
||
39 | * It loads both the Themes and Settings language files. |
||
40 | * Checks the session by GET or POST to verify the sent data. |
||
41 | * Requires the user not be a guest. (@todo what?) |
||
42 | * Accessed via ?action=admin;area=theme. |
||
43 | */ |
||
44 | function ThemesMain() |
||
45 | { |
||
46 | global $txt, $context, $sourcedir; |
||
47 | |||
48 | // Load the important language files... |
||
49 | loadLanguage('Themes'); |
||
50 | loadLanguage('Settings'); |
||
51 | loadLanguage('Drafts'); |
||
52 | |||
53 | // No funny business - guests only. |
||
54 | is_not_guest(); |
||
55 | |||
56 | require_once($sourcedir . '/Subs-Themes.php'); |
||
57 | |||
58 | // Default the page title to Theme Administration by default. |
||
59 | $context['page_title'] = $txt['themeadmin_title']; |
||
60 | |||
61 | // Theme administration, removal, choice, or installation... |
||
62 | $subActions = array( |
||
63 | 'admin' => 'ThemeAdmin', |
||
64 | 'list' => 'ThemeList', |
||
65 | 'reset' => 'SetThemeOptions', |
||
66 | 'options' => 'SetThemeOptions', |
||
67 | 'install' => 'ThemeInstall', |
||
68 | 'remove' => 'RemoveTheme', |
||
69 | 'pick' => 'PickTheme', |
||
70 | 'edit' => 'EditTheme', |
||
71 | 'enable' => 'EnableTheme', |
||
72 | 'copy' => 'CopyTemplate', |
||
73 | ); |
||
74 | |||
75 | // @todo Layout Settings? huh? |
||
76 | if (!empty($context['admin_menu_name'])) |
||
77 | { |
||
78 | $context[$context['admin_menu_name']]['tab_data'] = array( |
||
79 | 'title' => $txt['themeadmin_title'], |
||
80 | 'description' => $txt['themeadmin_description'], |
||
81 | 'tabs' => array( |
||
82 | 'admin' => array( |
||
83 | 'description' => $txt['themeadmin_admin_desc'], |
||
84 | ), |
||
85 | 'list' => array( |
||
86 | 'description' => $txt['themeadmin_list_desc'], |
||
87 | ), |
||
88 | 'reset' => array( |
||
89 | 'description' => $txt['themeadmin_reset_desc'], |
||
90 | ), |
||
91 | 'edit' => array( |
||
92 | 'description' => $txt['themeadmin_edit_desc'], |
||
93 | ), |
||
94 | ), |
||
95 | ); |
||
96 | } |
||
97 | |||
98 | // CRUD $subActions as needed. |
||
99 | call_integration_hook('integrate_manage_themes', array(&$subActions)); |
||
100 | |||
101 | // Whatever you decide to do, clean the minify cache. |
||
102 | cache_put_data('minimized_css', null); |
||
103 | |||
104 | // Follow the sa or just go to administration. |
||
105 | if (isset($_GET['sa']) && !empty($subActions[$_GET['sa']])) |
||
106 | call_helper($subActions[$_GET['sa']]); |
||
107 | |||
108 | else |
||
109 | call_helper($subActions['admin']); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * This function allows administration of themes and their settings, |
||
114 | * as well as global theme settings. |
||
115 | * - sets the settings theme_allow, theme_guests, and knownThemes. |
||
116 | * - requires the admin_forum permission. |
||
117 | * - accessed with ?action=admin;area=theme;sa=admin. |
||
118 | * |
||
119 | * Uses Themes template |
||
120 | * Uses Admin language file |
||
121 | */ |
||
122 | function ThemeAdmin() |
||
123 | { |
||
124 | global $context, $boarddir; |
||
125 | |||
126 | // Are handling any settings? |
||
127 | if (isset($_POST['save'])) |
||
128 | { |
||
129 | checkSession(); |
||
130 | validateToken('admin-tm'); |
||
131 | |||
132 | if (isset($_POST['options']['known_themes'])) |
||
133 | foreach ($_POST['options']['known_themes'] as $key => $id) |
||
134 | $_POST['options']['known_themes'][$key] = (int) $id; |
||
135 | |||
136 | else |
||
137 | fatal_lang_error('themes_none_selectable', false); |
||
138 | |||
139 | if (!in_array($_POST['options']['theme_guests'], $_POST['options']['known_themes'])) |
||
140 | fatal_lang_error('themes_default_selectable', false); |
||
141 | |||
142 | // Commit the new settings. |
||
143 | updateSettings(array( |
||
144 | 'theme_allow' => $_POST['options']['theme_allow'], |
||
145 | 'theme_guests' => $_POST['options']['theme_guests'], |
||
146 | 'knownThemes' => implode(',', $_POST['options']['known_themes']), |
||
147 | )); |
||
148 | if ((int) $_POST['theme_reset'] == 0 || in_array($_POST['theme_reset'], $_POST['options']['known_themes'])) |
||
149 | updateMemberData(null, array('id_theme' => (int) $_POST['theme_reset'])); |
||
150 | |||
151 | redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=admin'); |
||
152 | } |
||
153 | |||
154 | loadLanguage('Admin'); |
||
155 | isAllowedTo('admin_forum'); |
||
156 | loadTemplate('Themes'); |
||
157 | |||
158 | // List all enabled themes. |
||
159 | get_all_themes(true); |
||
160 | |||
161 | // Can we create a new theme? |
||
162 | $context['can_create_new'] = is_writable($boarddir . '/Themes'); |
||
163 | $context['new_theme_dir'] = substr(realpath($boarddir . '/Themes/default'), 0, -7); |
||
164 | |||
165 | // Look for a non existent theme directory. (ie theme87.) |
||
166 | $theme_dir = $boarddir . '/Themes/theme'; |
||
167 | $i = 1; |
||
168 | while (file_exists($theme_dir . $i)) |
||
169 | $i++; |
||
170 | |||
171 | $context['new_theme_name'] = 'theme' . $i; |
||
172 | |||
173 | // A bunch of tokens for a bunch of forms. |
||
174 | createToken('admin-tm'); |
||
175 | createToken('admin-t-file'); |
||
176 | createToken('admin-t-copy'); |
||
177 | createToken('admin-t-dir'); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * This function lists the available themes and provides an interface to reset |
||
182 | * the paths of all the installed themes. |
||
183 | */ |
||
184 | function ThemeList() |
||
185 | { |
||
186 | global $context, $boarddir, $boardurl, $smcFunc; |
||
187 | |||
188 | loadLanguage('Admin'); |
||
189 | isAllowedTo('admin_forum'); |
||
190 | |||
191 | if (isset($_REQUEST['th'])) |
||
192 | return SetThemeSettings(); |
||
193 | |||
194 | if (isset($_POST['save'])) |
||
195 | { |
||
196 | checkSession(); |
||
197 | validateToken('admin-tl'); |
||
198 | |||
199 | // Calling the almighty power of global vars! |
||
200 | get_installed_themes(); |
||
201 | |||
202 | $setValues = array(); |
||
203 | foreach ($context['themes'] as $id => $theme) |
||
204 | { |
||
205 | if (file_exists($_POST['reset_dir'] . '/' . basename($theme['theme_dir']))) |
||
206 | { |
||
207 | $setValues[] = array($id, 0, 'theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['theme_dir']))); |
||
208 | $setValues[] = array($id, 0, 'theme_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir'])); |
||
209 | $setValues[] = array($id, 0, 'images_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir']) . '/' . basename($theme['images_url'])); |
||
210 | } |
||
211 | |||
212 | if (isset($theme['base_theme_dir']) && file_exists($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir']))) |
||
213 | { |
||
214 | $setValues[] = array($id, 0, 'base_theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir']))); |
||
215 | $setValues[] = array($id, 0, 'base_theme_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir'])); |
||
216 | $setValues[] = array($id, 0, 'base_images_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir']) . '/' . basename($theme['base_images_url'])); |
||
217 | } |
||
218 | |||
219 | cache_put_data('theme_settings-' . $id, null, 90); |
||
220 | } |
||
221 | |||
222 | if (!empty($setValues)) |
||
223 | { |
||
224 | $smcFunc['db_insert']('replace', |
||
225 | '{db_prefix}themes', |
||
226 | array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||
227 | $setValues, |
||
228 | array('id_theme', 'variable', 'id_member') |
||
229 | ); |
||
230 | } |
||
231 | |||
232 | redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id']); |
||
233 | } |
||
234 | |||
235 | loadTemplate('Themes'); |
||
236 | |||
237 | // Get all installed themes. |
||
238 | get_installed_themes(); |
||
239 | |||
240 | $context['reset_dir'] = realpath($boarddir . '/Themes'); |
||
241 | $context['reset_url'] = $boardurl . '/Themes'; |
||
242 | |||
243 | $context['sub_template'] = 'list_themes'; |
||
244 | createToken('admin-tl'); |
||
245 | createToken('admin-tr', 'request'); |
||
246 | createToken('admin-tre', 'request'); |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * Administrative global settings. |
||
251 | */ |
||
252 | function SetThemeOptions() |
||
253 | { |
||
254 | global $txt, $context, $settings, $modSettings, $smcFunc; |
||
255 | |||
256 | $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (isset($_GET['id']) ? (int) $_GET['id'] : 0); |
||
257 | |||
258 | isAllowedTo('admin_forum'); |
||
259 | |||
260 | if (empty($_GET['th']) && empty($_GET['id'])) |
||
261 | { |
||
262 | $request = $smcFunc['db_query']('', ' |
||
263 | SELECT id_theme, variable, value |
||
264 | FROM {db_prefix}themes |
||
265 | WHERE variable IN ({string:name}, {string:theme_dir}) |
||
266 | AND id_member = {int:no_member}', |
||
267 | array( |
||
268 | 'no_member' => 0, |
||
269 | 'name' => 'name', |
||
270 | 'theme_dir' => 'theme_dir', |
||
271 | ) |
||
272 | ); |
||
273 | $context['themes'] = array(); |
||
274 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
275 | { |
||
276 | if (!isset($context['themes'][$row['id_theme']])) |
||
277 | $context['themes'][$row['id_theme']] = array( |
||
278 | 'id' => $row['id_theme'], |
||
279 | 'num_default_options' => 0, |
||
280 | 'num_members' => 0, |
||
281 | ); |
||
282 | $context['themes'][$row['id_theme']][$row['variable']] = $row['value']; |
||
283 | } |
||
284 | $smcFunc['db_free_result']($request); |
||
285 | |||
286 | $request = $smcFunc['db_query']('', ' |
||
287 | SELECT id_theme, COUNT(*) AS value |
||
288 | FROM {db_prefix}themes |
||
289 | WHERE id_member = {int:guest_member} |
||
290 | GROUP BY id_theme', |
||
291 | array( |
||
292 | 'guest_member' => -1, |
||
293 | ) |
||
294 | ); |
||
295 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
296 | $context['themes'][$row['id_theme']]['num_default_options'] = $row['value']; |
||
297 | $smcFunc['db_free_result']($request); |
||
298 | |||
299 | // Need to make sure we don't do custom fields. |
||
300 | $request = $smcFunc['db_query']('', ' |
||
301 | SELECT col_name |
||
302 | FROM {db_prefix}custom_fields', |
||
303 | array( |
||
304 | ) |
||
305 | ); |
||
306 | $customFields = array(); |
||
307 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
308 | $customFields[] = $row['col_name']; |
||
309 | $smcFunc['db_free_result']($request); |
||
310 | $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})'); |
||
311 | |||
312 | $request = $smcFunc['db_query']('themes_count', ' |
||
313 | SELECT COUNT(DISTINCT id_member) AS value, id_theme |
||
314 | FROM {db_prefix}themes |
||
315 | WHERE id_member > {int:no_member} |
||
316 | ' . $customFieldsQuery . ' |
||
317 | GROUP BY id_theme', |
||
318 | array( |
||
319 | 'no_member' => 0, |
||
320 | 'custom_fields' => empty($customFields) ? array() : $customFields, |
||
321 | ) |
||
322 | ); |
||
323 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
324 | $context['themes'][$row['id_theme']]['num_members'] = $row['value']; |
||
325 | $smcFunc['db_free_result']($request); |
||
326 | |||
327 | // There has to be a Settings template! |
||
328 | foreach ($context['themes'] as $k => $v) |
||
329 | if (empty($v['theme_dir']) || (!file_exists($v['theme_dir'] . '/Settings.template.php') && empty($v['num_members']))) |
||
330 | unset($context['themes'][$k]); |
||
331 | |||
332 | loadTemplate('Themes'); |
||
333 | $context['sub_template'] = 'reset_list'; |
||
334 | |||
335 | createToken('admin-stor', 'request'); |
||
336 | return; |
||
337 | } |
||
338 | |||
339 | // Submit? |
||
340 | if (isset($_POST['submit']) && empty($_POST['who'])) |
||
341 | { |
||
342 | checkSession(); |
||
343 | validateToken('admin-sto'); |
||
344 | |||
345 | if (empty($_POST['options'])) |
||
346 | $_POST['options'] = array(); |
||
347 | if (empty($_POST['default_options'])) |
||
348 | $_POST['default_options'] = array(); |
||
349 | |||
350 | // Set up the sql query. |
||
351 | $setValues = array(); |
||
352 | |||
353 | foreach ($_POST['options'] as $opt => $val) |
||
354 | $setValues[] = array(-1, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val); |
||
355 | |||
356 | $old_settings = array(); |
||
357 | foreach ($_POST['default_options'] as $opt => $val) |
||
358 | { |
||
359 | $old_settings[] = $opt; |
||
360 | |||
361 | $setValues[] = array(-1, 1, $opt, is_array($val) ? implode(',', $val) : $val); |
||
362 | } |
||
363 | |||
364 | // If we're actually inserting something.. |
||
365 | if (!empty($setValues)) |
||
366 | { |
||
367 | // Are there options in non-default themes set that should be cleared? |
||
368 | if (!empty($old_settings)) |
||
369 | $smcFunc['db_query']('', ' |
||
370 | DELETE FROM {db_prefix}themes |
||
371 | WHERE id_theme != {int:default_theme} |
||
372 | AND id_member = {int:guest_member} |
||
373 | AND variable IN ({array_string:old_settings})', |
||
374 | array( |
||
375 | 'default_theme' => 1, |
||
376 | 'guest_member' => -1, |
||
377 | 'old_settings' => $old_settings, |
||
378 | ) |
||
379 | ); |
||
380 | |||
381 | $smcFunc['db_insert']('replace', |
||
382 | '{db_prefix}themes', |
||
383 | array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||
384 | $setValues, |
||
385 | array('id_theme', 'variable', 'id_member') |
||
386 | ); |
||
387 | } |
||
388 | |||
389 | cache_put_data('theme_settings-' . $_GET['th'], null, 90); |
||
390 | cache_put_data('theme_settings-1', null, 90); |
||
391 | |||
392 | redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); |
||
393 | } |
||
394 | elseif (isset($_POST['submit']) && $_POST['who'] == 1) |
||
395 | { |
||
396 | checkSession(); |
||
397 | validateToken('admin-sto'); |
||
398 | |||
399 | $_POST['options'] = empty($_POST['options']) ? array() : $_POST['options']; |
||
400 | $_POST['options_master'] = empty($_POST['options_master']) ? array() : $_POST['options_master']; |
||
401 | $_POST['default_options'] = empty($_POST['default_options']) ? array() : $_POST['default_options']; |
||
402 | $_POST['default_options_master'] = empty($_POST['default_options_master']) ? array() : $_POST['default_options_master']; |
||
403 | |||
404 | $old_settings = array(); |
||
405 | foreach ($_POST['default_options'] as $opt => $val) |
||
406 | { |
||
407 | if ($_POST['default_options_master'][$opt] == 0) |
||
408 | continue; |
||
409 | elseif ($_POST['default_options_master'][$opt] == 1) |
||
410 | { |
||
411 | // Delete then insert for ease of database compatibility! |
||
412 | $smcFunc['db_query']('substring', ' |
||
413 | DELETE FROM {db_prefix}themes |
||
414 | WHERE id_theme = {int:default_theme} |
||
415 | AND id_member > {int:no_member} |
||
416 | AND variable = SUBSTRING({string:option}, 1, 255)', |
||
417 | array( |
||
418 | 'default_theme' => 1, |
||
419 | 'no_member' => 0, |
||
420 | 'option' => $opt, |
||
421 | ) |
||
422 | ); |
||
423 | $smcFunc['db_query']('substring', ' |
||
424 | INSERT INTO {db_prefix}themes |
||
425 | (id_member, id_theme, variable, value) |
||
426 | SELECT id_member, 1, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534) |
||
427 | FROM {db_prefix}members', |
||
428 | array( |
||
429 | 'option' => $opt, |
||
430 | 'value' => (is_array($val) ? implode(',', $val) : $val), |
||
431 | ) |
||
432 | ); |
||
433 | |||
434 | $old_settings[] = $opt; |
||
435 | } |
||
436 | elseif ($_POST['default_options_master'][$opt] == 2) |
||
437 | { |
||
438 | $smcFunc['db_query']('', ' |
||
439 | DELETE FROM {db_prefix}themes |
||
440 | WHERE variable = {string:option_name} |
||
441 | AND id_member > {int:no_member}', |
||
442 | array( |
||
443 | 'no_member' => 0, |
||
444 | 'option_name' => $opt, |
||
445 | ) |
||
446 | ); |
||
447 | } |
||
448 | } |
||
449 | |||
450 | // Delete options from other themes. |
||
451 | if (!empty($old_settings)) |
||
452 | $smcFunc['db_query']('', ' |
||
453 | DELETE FROM {db_prefix}themes |
||
454 | WHERE id_theme != {int:default_theme} |
||
455 | AND id_member > {int:no_member} |
||
456 | AND variable IN ({array_string:old_settings})', |
||
457 | array( |
||
458 | 'default_theme' => 1, |
||
459 | 'no_member' => 0, |
||
460 | 'old_settings' => $old_settings, |
||
461 | ) |
||
462 | ); |
||
463 | |||
464 | foreach ($_POST['options'] as $opt => $val) |
||
465 | { |
||
466 | if ($_POST['options_master'][$opt] == 0) |
||
467 | continue; |
||
468 | elseif ($_POST['options_master'][$opt] == 1) |
||
469 | { |
||
470 | // Delete then insert for ease of database compatibility - again! |
||
471 | $smcFunc['db_query']('substring', ' |
||
472 | DELETE FROM {db_prefix}themes |
||
473 | WHERE id_theme = {int:current_theme} |
||
474 | AND id_member > {int:no_member} |
||
475 | AND variable = SUBSTRING({string:option}, 1, 255)', |
||
476 | array( |
||
477 | 'current_theme' => $_GET['th'], |
||
478 | 'no_member' => 0, |
||
479 | 'option' => $opt, |
||
480 | ) |
||
481 | ); |
||
482 | $smcFunc['db_query']('substring', ' |
||
483 | INSERT INTO {db_prefix}themes |
||
484 | (id_member, id_theme, variable, value) |
||
485 | SELECT id_member, {int:current_theme}, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534) |
||
486 | FROM {db_prefix}members', |
||
487 | array( |
||
488 | 'current_theme' => $_GET['th'], |
||
489 | 'option' => $opt, |
||
490 | 'value' => (is_array($val) ? implode(',', $val) : $val), |
||
491 | ) |
||
492 | ); |
||
493 | } |
||
494 | elseif ($_POST['options_master'][$opt] == 2) |
||
495 | { |
||
496 | $smcFunc['db_query']('', ' |
||
497 | DELETE FROM {db_prefix}themes |
||
498 | WHERE variable = {string:option} |
||
499 | AND id_member > {int:no_member} |
||
500 | AND id_theme = {int:current_theme}', |
||
501 | array( |
||
502 | 'no_member' => 0, |
||
503 | 'current_theme' => $_GET['th'], |
||
504 | 'option' => $opt, |
||
505 | ) |
||
506 | ); |
||
507 | } |
||
508 | } |
||
509 | |||
510 | redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); |
||
511 | } |
||
512 | elseif (!empty($_GET['who']) && $_GET['who'] == 2) |
||
513 | { |
||
514 | checkSession('get'); |
||
515 | validateToken('admin-stor', 'request'); |
||
516 | |||
517 | // Don't delete custom fields!! |
||
518 | if ($_GET['th'] == 1) |
||
519 | { |
||
520 | $request = $smcFunc['db_query']('', ' |
||
521 | SELECT col_name |
||
522 | FROM {db_prefix}custom_fields', |
||
523 | array( |
||
524 | ) |
||
525 | ); |
||
526 | $customFields = array(); |
||
527 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
528 | $customFields[] = $row['col_name']; |
||
529 | $smcFunc['db_free_result']($request); |
||
530 | } |
||
531 | $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})'); |
||
532 | |||
533 | $smcFunc['db_query']('', ' |
||
534 | DELETE FROM {db_prefix}themes |
||
535 | WHERE id_member > {int:no_member} |
||
536 | AND id_theme = {int:current_theme} |
||
537 | ' . $customFieldsQuery, |
||
538 | array( |
||
539 | 'no_member' => 0, |
||
540 | 'current_theme' => $_GET['th'], |
||
541 | 'custom_fields' => empty($customFields) ? array() : $customFields, |
||
542 | ) |
||
543 | ); |
||
544 | |||
545 | redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset'); |
||
546 | } |
||
547 | |||
548 | $old_id = $settings['theme_id']; |
||
549 | $old_settings = $settings; |
||
550 | |||
551 | loadTheme($_GET['th'], false); |
||
552 | |||
553 | loadLanguage('Profile'); |
||
554 | // @todo Should we just move these options so they are no longer theme dependant? |
||
555 | loadLanguage('PersonalMessage'); |
||
556 | |||
557 | // Let the theme take care of the settings. |
||
558 | loadTemplate('Settings'); |
||
559 | loadSubTemplate('options'); |
||
560 | |||
561 | // Let mods hook into the theme options. |
||
562 | call_integration_hook('integrate_theme_options'); |
||
563 | |||
564 | $context['sub_template'] = 'set_options'; |
||
565 | $context['page_title'] = $txt['theme_settings']; |
||
566 | |||
567 | $context['options'] = $context['theme_options']; |
||
568 | $context['theme_settings'] = $settings; |
||
569 | |||
570 | if (empty($_REQUEST['who'])) |
||
571 | { |
||
572 | $request = $smcFunc['db_query']('', ' |
||
573 | SELECT variable, value |
||
574 | FROM {db_prefix}themes |
||
575 | WHERE id_theme IN (1, {int:current_theme}) |
||
576 | AND id_member = {int:guest_member}', |
||
577 | array( |
||
578 | 'current_theme' => $_GET['th'], |
||
579 | 'guest_member' => -1, |
||
580 | ) |
||
581 | ); |
||
582 | $context['theme_options'] = array(); |
||
583 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
584 | $context['theme_options'][$row['variable']] = $row['value']; |
||
585 | $smcFunc['db_free_result']($request); |
||
586 | |||
587 | $context['theme_options_reset'] = false; |
||
588 | } |
||
589 | else |
||
590 | { |
||
591 | $context['theme_options'] = array(); |
||
592 | $context['theme_options_reset'] = true; |
||
593 | } |
||
594 | |||
595 | foreach ($context['options'] as $i => $setting) |
||
596 | { |
||
597 | // Just skip separators |
||
598 | if (!is_array($setting)) |
||
599 | continue; |
||
600 | |||
601 | // Is this disabled? |
||
602 | if (isset($setting['enabled']) && $setting['enabled'] === false) |
||
603 | { |
||
604 | unset($context['options'][$i]); |
||
605 | continue; |
||
606 | } |
||
607 | |||
608 | if (!isset($setting['type']) || $setting['type'] == 'bool') |
||
609 | $context['options'][$i]['type'] = 'checkbox'; |
||
610 | elseif ($setting['type'] == 'int' || $setting['type'] == 'integer') |
||
611 | $context['options'][$i]['type'] = 'number'; |
||
612 | elseif ($setting['type'] == 'string') |
||
613 | $context['options'][$i]['type'] = 'text'; |
||
614 | |||
615 | if (isset($setting['options'])) |
||
616 | $context['options'][$i]['type'] = 'list'; |
||
617 | |||
618 | $context['options'][$i]['value'] = !isset($context['theme_options'][$setting['id']]) ? '' : $context['theme_options'][$setting['id']]; |
||
619 | } |
||
620 | |||
621 | // Restore the existing theme. |
||
622 | loadTheme($old_id, false); |
||
623 | $settings = $old_settings; |
||
624 | |||
625 | loadTemplate('Themes'); |
||
626 | createToken('admin-sto'); |
||
627 | } |
||
628 | |||
629 | /** |
||
630 | * Administrative global settings. |
||
631 | * - saves and requests global theme settings. ($settings) |
||
632 | * - loads the Admin language file. |
||
633 | * - calls ThemeAdmin() if no theme is specified. (the theme center.) |
||
634 | * - requires admin_forum permission. |
||
635 | * - accessed with ?action=admin;area=theme;sa=list&th=xx. |
||
636 | */ |
||
637 | function SetThemeSettings() |
||
638 | { |
||
639 | global $txt, $context, $settings, $modSettings, $smcFunc; |
||
640 | |||
641 | if (empty($_GET['th']) && empty($_GET['id'])) |
||
642 | return ThemeAdmin(); |
||
643 | |||
644 | $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; |
||
645 | |||
646 | // Select the best fitting tab. |
||
647 | $context[$context['admin_menu_name']]['current_subsection'] = 'list'; |
||
648 | |||
649 | loadLanguage('Admin'); |
||
650 | isAllowedTo('admin_forum'); |
||
651 | |||
652 | // Validate inputs/user. |
||
653 | if (empty($_GET['th'])) |
||
654 | fatal_lang_error('no_theme', false); |
||
655 | |||
656 | // Fetch the smiley sets... |
||
657 | $sets = explode(',', 'none,' . $modSettings['smiley_sets_known']); |
||
658 | $set_names = explode("\n", $txt['smileys_none'] . "\n" . $modSettings['smiley_sets_names']); |
||
659 | $context['smiley_sets'] = array( |
||
660 | '' => $txt['smileys_no_default'] |
||
661 | ); |
||
662 | foreach ($sets as $i => $set) |
||
663 | $context['smiley_sets'][$set] = $smcFunc['htmlspecialchars']($set_names[$i]); |
||
664 | |||
665 | $old_id = $settings['theme_id']; |
||
666 | $old_settings = $settings; |
||
667 | |||
668 | loadTheme($_GET['th'], false); |
||
669 | |||
670 | // Sadly we really do need to init the template. |
||
671 | loadSubTemplate('init', 'ignore'); |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
672 | |||
673 | // Also load the actual themes language file - in case of special settings. |
||
674 | loadLanguage('Settings', '', true, true); |
||
675 | |||
676 | // And the custom language strings... |
||
677 | loadLanguage('ThemeStrings', '', false, true); |
||
678 | |||
679 | // Let the theme take care of the settings. |
||
680 | loadTemplate('Settings'); |
||
681 | loadSubTemplate('settings'); |
||
682 | |||
683 | // Load the variants separately... |
||
684 | $settings['theme_variants'] = array(); |
||
685 | if (file_exists($settings['theme_dir'] . '/index.template.php')) |
||
686 | { |
||
687 | $file_contents = implode('', file($settings['theme_dir'] . '/index.template.php')); |
||
688 | if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches)) |
||
689 | eval('global $settings;' . $matches[0]); |
||
0 ignored issues
–
show
|
|||
690 | } |
||
691 | |||
692 | // Let mods hook into the theme settings. |
||
693 | call_integration_hook('integrate_theme_settings'); |
||
694 | |||
695 | // Submitting! |
||
696 | if (isset($_POST['save'])) |
||
697 | { |
||
698 | checkSession(); |
||
699 | validateToken('admin-sts'); |
||
700 | |||
701 | if (empty($_POST['options'])) |
||
702 | $_POST['options'] = array(); |
||
703 | if (empty($_POST['default_options'])) |
||
704 | $_POST['default_options'] = array(); |
||
705 | |||
706 | // Make sure items are cast correctly. |
||
707 | foreach ($context['theme_settings'] as $item) |
||
708 | { |
||
709 | // Disregard this item if this is just a separator. |
||
710 | if (!is_array($item)) |
||
711 | continue; |
||
712 | |||
713 | foreach (array('options', 'default_options') as $option) |
||
714 | { |
||
715 | if (!isset($_POST[$option][$item['id']])) |
||
716 | continue; |
||
717 | // Checkbox. |
||
718 | elseif (empty($item['type'])) |
||
719 | $_POST[$option][$item['id']] = $_POST[$option][$item['id']] ? 1 : 0; |
||
720 | // Number |
||
721 | elseif ($item['type'] == 'number') |
||
722 | $_POST[$option][$item['id']] = (int) $_POST[$option][$item['id']]; |
||
723 | } |
||
724 | } |
||
725 | |||
726 | // Set up the sql query. |
||
727 | $inserts = array(); |
||
728 | foreach ($_POST['options'] as $opt => $val) |
||
729 | $inserts[] = array(0, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val); |
||
730 | foreach ($_POST['default_options'] as $opt => $val) |
||
731 | $inserts[] = array(0, 1, $opt, is_array($val) ? implode(',', $val) : $val); |
||
732 | // If we're actually inserting something.. |
||
733 | if (!empty($inserts)) |
||
734 | { |
||
735 | $smcFunc['db_insert']('replace', |
||
736 | '{db_prefix}themes', |
||
737 | array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||
738 | $inserts, |
||
739 | array('id_member', 'id_theme', 'variable') |
||
740 | ); |
||
741 | } |
||
742 | |||
743 | cache_put_data('theme_settings-' . $_GET['th'], null, 90); |
||
744 | cache_put_data('theme_settings-1', null, 90); |
||
745 | |||
746 | // Invalidate the cache. |
||
747 | updateSettings(array('settings_updated' => time())); |
||
748 | |||
749 | redirectexit('action=admin;area=theme;sa=list;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id']); |
||
750 | } |
||
751 | |||
752 | $context['sub_template'] = 'set_settings'; |
||
753 | $context['page_title'] = $txt['theme_settings']; |
||
754 | |||
755 | foreach ($settings as $setting => $dummy) |
||
756 | { |
||
757 | if (!in_array($setting, array('theme_url', 'theme_dir', 'images_url', 'template_dirs'))) |
||
758 | $settings[$setting] = htmlspecialchars__recursive($settings[$setting]); |
||
759 | } |
||
760 | |||
761 | $context['settings'] = $context['theme_settings']; |
||
762 | $context['theme_settings'] = $settings; |
||
763 | |||
764 | foreach ($context['settings'] as $i => $setting) |
||
765 | { |
||
766 | // Separators are dummies, so leave them alone. |
||
767 | if (!is_array($setting)) |
||
768 | continue; |
||
769 | |||
770 | if (!isset($setting['type']) || $setting['type'] == 'bool') |
||
771 | $context['settings'][$i]['type'] = 'checkbox'; |
||
772 | elseif ($setting['type'] == 'int' || $setting['type'] == 'integer') |
||
773 | $context['settings'][$i]['type'] = 'number'; |
||
774 | elseif ($setting['type'] == 'string') |
||
775 | $context['settings'][$i]['type'] = 'text'; |
||
776 | |||
777 | if (isset($setting['options'])) |
||
778 | $context['settings'][$i]['type'] = 'list'; |
||
779 | |||
780 | $context['settings'][$i]['value'] = !isset($settings[$setting['id']]) ? '' : $settings[$setting['id']]; |
||
781 | } |
||
782 | |||
783 | // Do we support variants? |
||
784 | if (!empty($settings['theme_variants'])) |
||
785 | { |
||
786 | $context['theme_variants'] = array(); |
||
787 | foreach ($settings['theme_variants'] as $variant) |
||
788 | { |
||
789 | // Have any text, old chap? |
||
790 | $context['theme_variants'][$variant] = array( |
||
791 | 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant, |
||
792 | 'thumbnail' => !file_exists($settings['theme_dir'] . '/images/thumbnail.png') || file_exists($settings['theme_dir'] . '/images/thumbnail_' . $variant . '.png') ? $settings['images_url'] . '/thumbnail_' . $variant . '.png' : ($settings['images_url'] . '/thumbnail.png'), |
||
793 | ); |
||
794 | } |
||
795 | $context['default_variant'] = !empty($settings['default_variant']) && isset($context['theme_variants'][$settings['default_variant']]) ? $settings['default_variant'] : $settings['theme_variants'][0]; |
||
796 | } |
||
797 | |||
798 | // Restore the current theme. |
||
799 | loadTheme($old_id, false); |
||
800 | |||
801 | // Reinit just incase. |
||
802 | loadSubTemplate('init', 'ignore'); |
||
803 | |||
804 | $settings = $old_settings; |
||
805 | |||
806 | loadTemplate('Themes'); |
||
807 | |||
808 | // We like Kenny better than Token. |
||
809 | createToken('admin-sts'); |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * Remove a theme from the database. |
||
814 | * - removes an installed theme. |
||
815 | * - requires an administrator. |
||
816 | * - accessed with ?action=admin;area=theme;sa=remove. |
||
817 | */ |
||
818 | function RemoveTheme() |
||
819 | { |
||
820 | global $context; |
||
821 | |||
822 | checkSession('get'); |
||
823 | |||
824 | isAllowedTo('admin_forum'); |
||
825 | validateToken('admin-tr', 'request'); |
||
826 | |||
827 | // The theme's ID must be an integer. |
||
828 | $themeID = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; |
||
829 | |||
830 | // You can't delete the default theme! |
||
831 | if ($themeID == 1) |
||
832 | fatal_lang_error('no_access', false); |
||
833 | |||
834 | $theme_info = get_single_theme($themeID, array('theme_dir')); |
||
835 | |||
836 | // Remove it from the DB. |
||
837 | remove_theme($themeID); |
||
838 | |||
839 | // And remove all its files and folders too. |
||
840 | if (!empty($theme_info) && !empty($theme_info['theme_dir'])) |
||
841 | remove_dir($theme_info['theme_dir']); |
||
842 | |||
843 | // Go back to the list page. |
||
844 | redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'] . ';done=removing'); |
||
845 | } |
||
846 | |||
847 | /** |
||
848 | * Handles enabling/disabling a theme from the admin center |
||
849 | */ |
||
850 | function EnableTheme() |
||
851 | { |
||
852 | global $modSettings, $context; |
||
853 | |||
854 | checkSession('get'); |
||
855 | |||
856 | isAllowedTo('admin_forum'); |
||
857 | validateToken('admin-tre', 'request'); |
||
858 | |||
859 | // The theme's ID must be an string. |
||
860 | $themeID = isset($_GET['th']) ? (string) trim($_GET['th']) : (string) trim($_GET['id']); |
||
861 | |||
862 | // Get the current list. |
||
863 | $enableThemes = explode(',', $modSettings['enableThemes']); |
||
864 | |||
865 | // Are we disabling it? |
||
866 | if (isset($_GET['disabled'])) |
||
867 | $enableThemes = array_diff($enableThemes, array($themeID)); |
||
868 | |||
869 | // Nope? then enable it! |
||
870 | else |
||
871 | $enableThemes[] = (string) $themeID; |
||
872 | |||
873 | // Update the setting. |
||
874 | $enableThemes = strtr(implode(',', $enableThemes), array(',,' => ',')); |
||
875 | updateSettings(array('enableThemes' => $enableThemes)); |
||
876 | |||
877 | // Done! |
||
878 | redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'] . ';done=' . (isset($_GET['disabled']) ? 'disabling' : 'enabling')); |
||
879 | } |
||
880 | |||
881 | /** |
||
882 | * Determines if a user can change their theme. |
||
883 | * |
||
884 | * @param int $id_member |
||
885 | * @param int $id_theme |
||
886 | * |
||
887 | * @return bool |
||
888 | */ |
||
889 | function canPickTheme($id_member, $id_theme) |
||
890 | { |
||
891 | global $modSettings, $user_info; |
||
892 | |||
893 | return |
||
894 | allowedTo($user_info['id'] == $id_member ? 'profile_extra_own' : 'profile_extra_any') |
||
895 | && ($id_theme == 0 || (allowedTo('admin_forum') || in_array($id_theme, explode(',', $modSettings['knownThemes']))) && in_array($id_theme, explode(',', $modSettings['enableThemes']))) |
||
896 | && (!empty($modSettings['theme_allow']) || allowedTo('admin_forum')); |
||
897 | } |
||
898 | |||
899 | /** |
||
900 | * Choose a theme from a list. |
||
901 | * allows a user to pick a new theme with an interface. |
||
902 | * - uses the Themes template. (pick sub template.) |
||
903 | * - accessed with ?action=theme;sa=pick. |
||
904 | */ |
||
905 | function PickTheme() |
||
906 | { |
||
907 | global $txt, $context, $modSettings, $user_info, $language, $smcFunc, $settings, $scripturl; |
||
908 | |||
909 | loadLanguage('Profile'); |
||
910 | loadTemplate('Themes'); |
||
911 | |||
912 | // Build the link tree. |
||
913 | $context['linktree'][] = array( |
||
914 | 'url' => $scripturl . '?action=theme;sa=pick;u=' . (!empty($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0), |
||
915 | 'name' => $txt['theme_pick'], |
||
916 | ); |
||
917 | $context['default_theme_id'] = $modSettings['theme_default']; |
||
918 | $_SESSION['id_theme'] = 0; |
||
919 | if (!isset($_REQUEST['u'])) |
||
920 | $_REQUEST['u'] = $user_info['id']; |
||
921 | |||
922 | // Have we made a decision, or are we just browsing? |
||
923 | if (isset($_POST['save'])) |
||
924 | { |
||
925 | checkSession(); |
||
926 | validateToken('pick-th'); |
||
927 | |||
928 | $id_theme = (int) key($_POST['save']); |
||
929 | if (isset($_POST['vrt'][$id_theme])) |
||
930 | $variant = $_POST['vrt'][$id_theme]; |
||
931 | |||
932 | if (canPickTheme((int) $_REQUEST['u'], $id_theme)) |
||
933 | { |
||
934 | // An identifier of zero means that the user wants the forum default theme. |
||
935 | updateMemberData((int) $_REQUEST['u'], array('id_theme' => $id_theme)); |
||
936 | |||
937 | if (!empty($variant)) |
||
938 | { |
||
939 | // Set the identifier to the forum default. |
||
940 | if (isset($id_theme) && $id_theme == 0) |
||
941 | $id_theme = $modSettings['theme_guests']; |
||
942 | |||
943 | $smcFunc['db_insert']('replace', |
||
944 | '{db_prefix}themes', |
||
945 | array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||
946 | array($id_theme, (int) $_REQUEST['u'], 'theme_variant', $variant), |
||
947 | array('id_theme', 'id_member', 'variable') |
||
948 | ); |
||
949 | cache_put_data('theme_settings-' . $id_theme . ':' . (int) $_REQUEST['u'], null, 90); |
||
950 | |||
951 | if ($user_info['id'] == $_REQUEST['u']) |
||
952 | $_SESSION['id_variant'] = 0; |
||
953 | } |
||
954 | |||
955 | redirectexit('action=profile;u=' . (int) $_REQUEST['u'] . ';area=theme'); |
||
956 | } |
||
957 | } |
||
958 | |||
959 | // Figure out who the member of the minute is, and what theme they've chosen. |
||
960 | if (!isset($_REQUEST['u']) || !allowedTo('admin_forum')) |
||
961 | { |
||
962 | $context['current_member'] = $user_info['id']; |
||
963 | $context['current_theme'] = $user_info['theme']; |
||
964 | } |
||
965 | else |
||
966 | { |
||
967 | $context['current_member'] = (int) $_REQUEST['u']; |
||
968 | |||
969 | $request = $smcFunc['db_query']('', ' |
||
970 | SELECT id_theme |
||
971 | FROM {db_prefix}members |
||
972 | WHERE id_member = {int:current_member} |
||
973 | LIMIT 1', |
||
974 | array( |
||
975 | 'current_member' => $context['current_member'], |
||
976 | ) |
||
977 | ); |
||
978 | list ($context['current_theme']) = $smcFunc['db_fetch_row']($request); |
||
979 | $smcFunc['db_free_result']($request); |
||
980 | } |
||
981 | |||
982 | // Get the theme name and descriptions. |
||
983 | $context['available_themes'] = array(); |
||
984 | if (!empty($modSettings['knownThemes'])) |
||
985 | { |
||
986 | $request = $smcFunc['db_query']('', ' |
||
987 | SELECT id_theme, variable, value |
||
988 | FROM {db_prefix}themes |
||
989 | WHERE variable IN ({literal:name}, {literal:theme_url}, {literal:theme_dir}, {literal:images_url}, {literal:disable_user_variant})' . (!allowedTo('admin_forum') ? ' |
||
990 | AND id_theme IN ({array_int:known_themes})' : '') . ' |
||
991 | AND id_theme != {int:default_theme} |
||
992 | AND id_member = {int:no_member} |
||
993 | AND id_theme IN ({array_int:enable_themes})', |
||
994 | array( |
||
995 | 'default_theme' => 0, |
||
996 | 'no_member' => 0, |
||
997 | 'known_themes' => explode(',', $modSettings['knownThemes']), |
||
998 | 'enable_themes' => explode(',', $modSettings['enableThemes']), |
||
999 | ) |
||
1000 | ); |
||
1001 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
1002 | { |
||
1003 | if (!isset($context['available_themes'][$row['id_theme']])) |
||
1004 | $context['available_themes'][$row['id_theme']] = array( |
||
1005 | 'id' => $row['id_theme'], |
||
1006 | 'selected' => $context['current_theme'] == $row['id_theme'], |
||
1007 | 'num_users' => 0 |
||
1008 | ); |
||
1009 | $context['available_themes'][$row['id_theme']][$row['variable']] = $row['value']; |
||
1010 | } |
||
1011 | $smcFunc['db_free_result']($request); |
||
1012 | } |
||
1013 | |||
1014 | // Okay, this is a complicated problem: the default theme is 1, but they aren't allowed to access 1! |
||
1015 | if (!isset($context['available_themes'][$modSettings['theme_guests']])) |
||
1016 | { |
||
1017 | $context['available_themes'][0] = array( |
||
1018 | 'num_users' => 0 |
||
1019 | ); |
||
1020 | $guest_theme = 0; |
||
1021 | } |
||
1022 | else |
||
1023 | $guest_theme = $modSettings['theme_guests']; |
||
1024 | |||
1025 | $request = $smcFunc['db_query']('', ' |
||
1026 | SELECT id_theme, COUNT(*) AS the_count |
||
1027 | FROM {db_prefix}members |
||
1028 | GROUP BY id_theme |
||
1029 | ORDER BY id_theme DESC', |
||
1030 | array( |
||
1031 | ) |
||
1032 | ); |
||
1033 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
1034 | { |
||
1035 | // Figure out which theme it is they are REALLY using. |
||
1036 | if (!empty($modSettings['knownThemes']) && !in_array($row['id_theme'], explode(',', $modSettings['knownThemes']))) |
||
1037 | $row['id_theme'] = $guest_theme; |
||
1038 | elseif (empty($modSettings['theme_allow'])) |
||
1039 | $row['id_theme'] = $guest_theme; |
||
1040 | |||
1041 | if (isset($context['available_themes'][$row['id_theme']])) |
||
1042 | $context['available_themes'][$row['id_theme']]['num_users'] += $row['the_count']; |
||
1043 | else |
||
1044 | $context['available_themes'][$guest_theme]['num_users'] += $row['the_count']; |
||
1045 | } |
||
1046 | $smcFunc['db_free_result']($request); |
||
1047 | |||
1048 | // Get any member variant preferences. |
||
1049 | $variant_preferences = array(); |
||
1050 | if ($context['current_member'] > 0) |
||
1051 | { |
||
1052 | $request = $smcFunc['db_query']('', ' |
||
1053 | SELECT id_theme, value |
||
1054 | FROM {db_prefix}themes |
||
1055 | WHERE variable = {string:theme_variant} |
||
1056 | AND id_member IN ({array_int:id_member}) |
||
1057 | ORDER BY id_member ASC', |
||
1058 | array( |
||
1059 | 'theme_variant' => 'theme_variant', |
||
1060 | 'id_member' => isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'pick' ? array(-1, $context['current_member']) : array(-1), |
||
1061 | ) |
||
1062 | ); |
||
1063 | while ($row = $smcFunc['db_fetch_assoc']($request)) |
||
1064 | $variant_preferences[$row['id_theme']] = $row['value']; |
||
1065 | $smcFunc['db_free_result']($request); |
||
1066 | } |
||
1067 | |||
1068 | // Save the setting first. |
||
1069 | $current_images_url = $settings['images_url']; |
||
1070 | $current_theme_variants = !empty($settings['theme_variants']) ? $settings['theme_variants'] : array(); |
||
1071 | |||
1072 | foreach ($context['available_themes'] as $id_theme => $theme_data) |
||
1073 | { |
||
1074 | // Don't try to load the forum or board default theme's data... it doesn't have any! |
||
1075 | if ($id_theme == 0) |
||
1076 | continue; |
||
1077 | |||
1078 | // The thumbnail needs the correct path. |
||
1079 | $settings['images_url'] = &$theme_data['images_url']; |
||
1080 | |||
1081 | if (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php')) |
||
1082 | include($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php'); |
||
1083 | elseif (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php')) |
||
1084 | include($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php'); |
||
1085 | else |
||
1086 | { |
||
1087 | $txt['theme_thumbnail_href'] = $theme_data['images_url'] . '/thumbnail.png'; |
||
1088 | $txt['theme_description'] = ''; |
||
1089 | } |
||
1090 | |||
1091 | $context['available_themes'][$id_theme]['thumbnail_href'] = sprintf($txt['theme_thumbnail_href'], $settings['images_url']); |
||
1092 | $context['available_themes'][$id_theme]['description'] = $txt['theme_description']; |
||
1093 | |||
1094 | // Are there any variants? |
||
1095 | $context['available_themes'][$id_theme]['variants'] = array(); |
||
1096 | if (file_exists($theme_data['theme_dir'] . '/index.template.php') && (empty($theme_data['disable_user_variant']) || allowedTo('admin_forum'))) |
||
1097 | { |
||
1098 | $file_contents = implode('', file($theme_data['theme_dir'] . '/index.template.php')); |
||
1099 | if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches)) |
||
1100 | { |
||
1101 | $settings['theme_variants'] = array(); |
||
1102 | |||
1103 | // Fill settings up. |
||
1104 | eval('global $settings;' . $matches[0]); |
||
0 ignored issues
–
show
|
|||
1105 | |||
1106 | if (!empty($settings['theme_variants'])) |
||
1107 | { |
||
1108 | loadLanguage('Settings'); |
||
1109 | |||
1110 | foreach ($settings['theme_variants'] as $variant) |
||
1111 | $context['available_themes'][$id_theme]['variants'][$variant] = array( |
||
1112 | 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant, |
||
1113 | 'thumbnail' => !file_exists($theme_data['theme_dir'] . '/images/thumbnail.png') || file_exists($theme_data['theme_dir'] . '/images/thumbnail_' . $variant . '.png') ? $theme_data['images_url'] . '/thumbnail_' . $variant . '.png' : ($theme_data['images_url'] . '/thumbnail.png'), |
||
1114 | ); |
||
1115 | |||
1116 | $context['available_themes'][$id_theme]['selected_variant'] = isset($_GET['vrt']) ? $_GET['vrt'] : (!empty($variant_preferences[$id_theme]) ? $variant_preferences[$id_theme] : (!empty($settings['default_variant']) ? $settings['default_variant'] : $settings['theme_variants'][0])); |
||
1117 | if (!isset($context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail'])) |
||
1118 | $context['available_themes'][$id_theme]['selected_variant'] = $settings['theme_variants'][0]; |
||
1119 | |||
1120 | $context['available_themes'][$id_theme]['thumbnail_href'] = $context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail']; |
||
1121 | // Allow themes to override the text. |
||
1122 | $context['available_themes'][$id_theme]['pick_label'] = isset($txt['variant_pick']) ? $txt['variant_pick'] : $txt['theme_pick_variant']; |
||
1123 | } |
||
1124 | } |
||
1125 | } |
||
1126 | } |
||
1127 | // Then return it. |
||
1128 | addJavaScriptVar( |
||
1129 | 'oThemeVariants', |
||
1130 | json_encode(array_map( |
||
1131 | function($theme) |
||
1132 | { |
||
1133 | return $theme['variants']; |
||
1134 | }, |
||
1135 | $context['available_themes'] |
||
1136 | )) |
||
1137 | ); |
||
1138 | loadJavaScriptFile('profile.js', array('defer' => false, 'minimize' => true), 'smf_profile'); |
||
1139 | $settings['images_url'] = $current_images_url; |
||
1140 | $settings['theme_variants'] = $current_theme_variants; |
||
1141 | |||
1142 | // As long as we're not doing the default theme... |
||
1143 | if (!isset($_REQUEST['u']) || $_REQUEST['u'] >= 0) |
||
1144 | { |
||
1145 | if ($guest_theme != 0) |
||
1146 | $context['available_themes'][0] = $context['available_themes'][$guest_theme]; |
||
1147 | |||
1148 | $context['available_themes'][0]['id'] = 0; |
||
1149 | $context['available_themes'][0]['name'] = $txt['theme_forum_default']; |
||
1150 | $context['available_themes'][0]['selected'] = $context['current_theme'] == 0; |
||
1151 | $context['available_themes'][0]['description'] = $txt['theme_global_description']; |
||
1152 | } |
||
1153 | |||
1154 | ksort($context['available_themes']); |
||
1155 | |||
1156 | $context['page_title'] = $txt['theme_pick']; |
||
1157 | $context['sub_template'] = 'pick'; |
||
1158 | createToken('pick-th'); |
||
1159 | } |
||
1160 | |||
1161 | /** |
||
1162 | * Installs new themes, calls the respective function according to the install type. |
||
1163 | * - puts themes in $boardurl/Themes. |
||
1164 | * - assumes the gzip has a root directory in it. (ie default.) |
||
1165 | * Requires admin_forum. |
||
1166 | * Accessed with ?action=admin;area=theme;sa=install. |
||
1167 | */ |
||
1168 | function ThemeInstall() |
||
1169 | { |
||
1170 | global $sourcedir, $txt, $context, $boarddir, $boardurl; |
||
1171 | global $themedir, $themeurl, $smcFunc; |
||
1172 | |||
1173 | checkSession('request'); |
||
1174 | isAllowedTo('admin_forum'); |
||
1175 | |||
1176 | require_once($sourcedir . '/Subs-Package.php'); |
||
1177 | |||
1178 | // Make it easier to change the path and url. |
||
1179 | $themedir = $boarddir . '/Themes'; |
||
1180 | $themeurl = $boardurl . '/Themes'; |
||
1181 | |||
1182 | loadTemplate('Themes'); |
||
1183 | |||
1184 | $subActions = array( |
||
1185 | 'file' => 'InstallFile', |
||
1186 | 'copy' => 'InstallCopy', |
||
1187 | 'dir' => 'InstallDir', |
||
1188 | ); |
||
1189 | |||
1190 | // Is there a function to call? |
||
1191 | if (isset($_GET['do']) && !empty($_GET['do']) && isset($subActions[$_GET['do']])) |
||
1192 | { |
||
1193 | $action = $smcFunc['htmlspecialchars'](trim($_GET['do'])); |
||
1194 | |||
1195 | // Got any info from the specific form? |
||
1196 | if (!isset($_POST['save_' . $action])) |
||
1197 | fatal_lang_error('theme_install_no_action', false); |
||
1198 | |||
1199 | validateToken('admin-t-' . $action); |
||
1200 | |||
1201 | // Hopefully the themes directory is writable, or we might have a problem. |
||
1202 | if (!is_writable($themedir)) |
||
1203 | fatal_lang_error('theme_install_write_error', 'critical'); |
||
1204 | |||
1205 | // Call the function and handle the result. |
||
1206 | $result = $subActions[$action](); |
||
1207 | |||
1208 | // Everything went better than expected! |
||
1209 | if (!empty($result)) |
||
1210 | { |
||
1211 | $context['sub_template'] = 'installed'; |
||
1212 | $context['page_title'] = $txt['theme_installed']; |
||
1213 | $context['installed_theme'] = $result; |
||
1214 | } |
||
1215 | } |
||
1216 | |||
1217 | // Nope, show a nice error. |
||
1218 | else |
||
1219 | fatal_lang_error('theme_install_no_action', false); |
||
1220 | } |
||
1221 | |||
1222 | /** |
||
1223 | * Installs a theme from a theme package. |
||
1224 | * |
||
1225 | * Stores the theme files on a temp dir, on success it renames the dir to the new theme's name. Ends execution with fatal_lang_error() on any error. |
||
1226 | * |
||
1227 | * @return array The newly created theme's info. |
||
1228 | */ |
||
1229 | function InstallFile() |
||
1230 | { |
||
1231 | global $themedir, $themeurl, $context; |
||
1232 | |||
1233 | // Set a temp dir for dumping all required files on it. |
||
1234 | $dirtemp = $themedir . '/temp'; |
||
1235 | |||
1236 | // Make sure the temp dir doesn't already exist |
||
1237 | if (file_exists($dirtemp)) |
||
1238 | remove_dir($dirtemp); |
||
1239 | |||
1240 | // Create the temp dir. |
||
1241 | mkdir($dirtemp, 0777); |
||
1242 | |||
1243 | // Hopefully the temp directory is writable, or we might have a problem. |
||
1244 | if (!is_writable($dirtemp)) |
||
1245 | { |
||
1246 | // Lets give it a try. |
||
1247 | smf_chmod($dirtemp, '0755'); |
||
1248 | |||
1249 | // How about now? |
||
1250 | if (!is_writable($dirtemp)) |
||
1251 | fatal_lang_error('theme_install_write_error', 'critical'); |
||
1252 | } |
||
1253 | |||
1254 | // This happens when the admin session is gone and the user has to login again. |
||
1255 | if (!isset($_FILES) || !isset($_FILES['theme_gz']) || empty($_FILES['theme_gz'])) |
||
1256 | redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']); |
||
1257 | |||
1258 | // Another error check layer, something went wrong with the upload. |
||
1259 | if (isset($_FILES['theme_gz']['error']) && $_FILES['theme_gz']['error'] != 0) |
||
1260 | fatal_lang_error('theme_install_error_file_' . $_FILES['theme_gz']['error'], false); |
||
1261 | |||
1262 | // Get the theme's name. |
||
1263 | $name = pathinfo($_FILES['theme_gz']['name'], PATHINFO_FILENAME); |
||
1264 | $name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/', '/\.tar$/'), array('_', '.', '', ''), $name); |
||
1265 | |||
1266 | // Start setting some vars. |
||
1267 | $context['to_install'] = array( |
||
1268 | 'theme_dir' => $themedir . '/' . $name, |
||
1269 | 'theme_url' => $themeurl . '/' . $name, |
||
1270 | 'images_url' => $themeurl . '/' . $name . '/images', |
||
1271 | 'name' => $name, |
||
1272 | ); |
||
1273 | |||
1274 | // Extract the file on the proper themes dir. |
||
1275 | $extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $dirtemp, false, true); |
||
1276 | |||
1277 | if ($extracted) |
||
1278 | { |
||
1279 | // Read its info form the XML file. |
||
1280 | $theme_info = get_theme_info($dirtemp); |
||
1281 | $context['to_install'] += $theme_info; |
||
1282 | |||
1283 | // Install the theme. theme_install() will return the new installed ID. |
||
1284 | $context['to_install']['id'] = theme_install($context['to_install']); |
||
1285 | |||
1286 | // Rename the temp dir to the actual theme name. |
||
1287 | rename($dirtemp, $context['to_install']['theme_dir']); |
||
1288 | |||
1289 | // return all the info. |
||
1290 | return $context['to_install']; |
||
1291 | } |
||
1292 | |||
1293 | else |
||
1294 | fatal_lang_error('theme_install_error_title', false); |
||
1295 | } |
||
1296 | |||
1297 | /** |
||
1298 | * Makes a copy from the default theme, assigns a name for it and installs it. |
||
1299 | * |
||
1300 | * Creates a new .xml file containing all the theme's info. |
||
1301 | * |
||
1302 | * @return array The newly created theme's info. |
||
1303 | */ |
||
1304 | function InstallCopy() |
||
1305 | { |
||
1306 | global $themedir, $themeurl, $settings, $smcFunc, $context; |
||
1307 | |||
1308 | // There's gotta be something to work with. |
||
1309 | if (!isset($_REQUEST['copy']) || empty($_REQUEST['copy'])) |
||
1310 | fatal_lang_error('theme_install_error_title', false); |
||
1311 | |||
1312 | // Get a cleaner version. |
||
1313 | $name = preg_replace('~[^A-Za-z0-9_\- ]~', '', $_REQUEST['copy']); |
||
1314 | |||
1315 | // Is there a theme already named like this? |
||
1316 | if (file_exists($themedir . '/' . $name)) |
||
1317 | fatal_lang_error('theme_install_already_dir', false); |
||
1318 | |||
1319 | // This is a brand new theme so set all possible values. |
||
1320 | $context['to_install'] = array( |
||
1321 | 'theme_dir' => $themedir . '/' . $name, |
||
1322 | 'theme_url' => $themeurl . '/' . $name, |
||
1323 | 'name' => $name, |
||
1324 | 'images_url' => $themeurl . '/' . $name . '/images', |
||
1325 | 'version' => '1.0', |
||
1326 | 'install_for' => '2.1 - 2.1.99, ' . SMF_VERSION, |
||
1327 | 'based_on' => '', |
||
1328 | 'based_on_dir' => $themedir . '/default', |
||
1329 | 'theme_layers' => 'html,body', |
||
1330 | 'theme_templates' => 'index', |
||
1331 | ); |
||
1332 | |||
1333 | // Create the specific dir. |
||
1334 | umask(0); |
||
1335 | mkdir($context['to_install']['theme_dir'], 0777); |
||
1336 | |||
1337 | // Buy some time. |
||
1338 | @set_time_limit(600); |
||
1339 | if (function_exists('apache_reset_timeout')) |
||
1340 | @apache_reset_timeout(); |
||
1341 | |||
1342 | // Create subdirectories for css and javascript files. |
||
1343 | mkdir($context['to_install']['theme_dir'] . '/css', 0777); |
||
1344 | mkdir($context['to_install']['theme_dir'] . '/scripts', 0777); |
||
1345 | |||
1346 | // Create subdirectory for language files |
||
1347 | mkdir($context['to_install']['theme_dir'] . '/languages', 0777); |
||
1348 | |||
1349 | // Copy over the default non-theme files. |
||
1350 | $to_copy = array( |
||
1351 | '/index.php', |
||
1352 | '/index.template.php', |
||
1353 | '/css/admin.css', |
||
1354 | '/css/calendar.css', |
||
1355 | '/css/calendar.rtl.css', |
||
1356 | '/css/index.css', |
||
1357 | '/css/responsive.css', |
||
1358 | '/css/rtl.css', |
||
1359 | '/scripts/theme.js', |
||
1360 | '/languages/index.php', |
||
1361 | '/languages/Settings.english.php', |
||
1362 | ); |
||
1363 | |||
1364 | foreach ($to_copy as $file) |
||
1365 | { |
||
1366 | copy($settings['default_theme_dir'] . $file, $context['to_install']['theme_dir'] . $file); |
||
1367 | smf_chmod($context['to_install']['theme_dir'] . $file, 0777); |
||
1368 | } |
||
1369 | |||
1370 | // And now the entire images directory! |
||
1371 | copytree($settings['default_theme_dir'] . '/images', $context['to_install']['theme_dir'] . '/images'); |
||
1372 | package_flush_cache(); |
||
1373 | |||
1374 | // Any data from the default theme that we want? |
||
1375 | foreach (get_single_theme(1, array('theme_layers', 'theme_templates')) as $variable => $value) |
||
1376 | if ($variable == 'theme_templates' || $variable == 'theme_layers') |
||
1377 | $context['to_install'][$variable] = $value; |
||
1378 | |||
1379 | // Lets add a theme_info.xml to this theme. |
||
1380 | $xml_info = '<' . '?xml version="1.0"?' . '> |
||
1381 | <theme-info xmlns="http://www.simplemachines.org/xml/theme-info" xmlns:smf="http://www.simplemachines.org/"> |
||
1382 | <!-- For the id, always use something unique - put your name, a colon, and then the package name. --> |
||
1383 | <id>smf:' . $smcFunc['strtolower']($context['to_install']['name']) . '</id> |
||
1384 | <!-- The theme\'s version, please try to use semantic versioning. --> |
||
1385 | <version>1.0</version> |
||
1386 | <!-- Install for, the SMF versions this theme was designed for. Uses the same wildcards used in the packager manager. This field is mandatory. --> |
||
1387 | <install for="' . $context['to_install']['install_for'] . '" /> |
||
1388 | <!-- Theme name, used purely for aesthetics. --> |
||
1389 | <name>' . $context['to_install']['name'] . '</name> |
||
1390 | <!-- Author: your email address or contact information. The name attribute is optional. --> |
||
1391 | <author name="Simple Machines">[email protected]</author> |
||
1392 | <!-- Website... where to get updates and more information. --> |
||
1393 | <website>https://www.simplemachines.org/</website> |
||
1394 | <!-- Template layers to use, defaults to "html,body". --> |
||
1395 | <layers>' . $context['to_install']['theme_layers'] . '</layers> |
||
1396 | <!-- Templates to load on startup. Default is "index". --> |
||
1397 | <templates>' . $context['to_install']['theme_templates'] . '</templates> |
||
1398 | <!-- Base this theme off another? Default is blank, or no. It could be "default". --> |
||
1399 | <based-on></based-on> |
||
1400 | </theme-info>'; |
||
1401 | |||
1402 | // Now write it. |
||
1403 | $fp = @fopen($context['to_install']['theme_dir'] . '/theme_info.xml', 'w+'); |
||
1404 | if ($fp) |
||
0 ignored issues
–
show
|
|||
1405 | { |
||
1406 | fwrite($fp, $xml_info); |
||
1407 | fclose($fp); |
||
1408 | } |
||
1409 | |||
1410 | // Install the theme. theme_install() will take care of possible errors. |
||
1411 | $context['to_install']['id'] = theme_install($context['to_install']); |
||
1412 | |||
1413 | // return the info. |
||
1414 | return $context['to_install']; |
||
1415 | } |
||
1416 | |||
1417 | /** |
||
1418 | * Install a theme from a specific dir |
||
1419 | * |
||
1420 | * Assumes the dir is located on the main Themes dir. Ends execution with fatal_lang_error() on any error. |
||
1421 | * |
||
1422 | * @return array The newly created theme's info. |
||
1423 | */ |
||
1424 | function InstallDir() |
||
1425 | { |
||
1426 | global $themedir, $themeurl, $context; |
||
1427 | |||
1428 | // Cannot use the theme dir as a theme dir. |
||
1429 | if (!isset($_REQUEST['theme_dir']) || empty($_REQUEST['theme_dir']) || rtrim(realpath($_REQUEST['theme_dir']), '/\\') == realpath($themedir)) |
||
1430 | fatal_lang_error('theme_install_invalid_dir', false); |
||
1431 | |||
1432 | // Check is there is "something" on the dir. |
||
1433 | elseif (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml')) |
||
1434 | fatal_lang_error('theme_install_error', false); |
||
1435 | |||
1436 | $name = basename($_REQUEST['theme_dir']); |
||
1437 | $name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $name); |
||
1438 | |||
1439 | // All good! set some needed vars. |
||
1440 | $context['to_install'] = array( |
||
1441 | 'theme_dir' => $_REQUEST['theme_dir'], |
||
1442 | 'theme_url' => $themeurl . '/' . $name, |
||
1443 | 'name' => $name, |
||
1444 | 'images_url' => $themeurl . '/' . $name . '/images', |
||
1445 | ); |
||
1446 | |||
1447 | // Read its info form the XML file. |
||
1448 | $theme_info = get_theme_info($context['to_install']['theme_dir']); |
||
1449 | $context['to_install'] += $theme_info; |
||
1450 | |||
1451 | // Install the theme. theme_install() will take care of possible errors. |
||
1452 | $context['to_install']['id'] = theme_install($context['to_install']); |
||
1453 | |||
1454 | // return the info. |
||
1455 | return $context['to_install']; |
||
1456 | } |
||
1457 | |||
1458 | /** |
||
1459 | * Possibly the simplest and best example of how to use the template system. |
||
1460 | * - allows the theme to take care of actions. |
||
1461 | * - happens if $settings['catch_action'] is set and action isn't found |
||
1462 | * in the action array. |
||
1463 | * - can use a template, layers, sub_template, filename, and/or function. |
||
1464 | */ |
||
1465 | function WrapAction() |
||
1466 | { |
||
1467 | global $context, $settings; |
||
1468 | |||
1469 | // Load any necessary template(s)? |
||
1470 | if (isset($settings['catch_action']['template'])) |
||
1471 | { |
||
1472 | // Load both the template and language file. (but don't fret if the language file isn't there...) |
||
1473 | loadTemplate($settings['catch_action']['template']); |
||
1474 | loadLanguage($settings['catch_action']['template'], '', false); |
||
1475 | } |
||
1476 | |||
1477 | // Any special layers? |
||
1478 | if (isset($settings['catch_action']['layers'])) |
||
1479 | $context['template_layers'] = $settings['catch_action']['layers']; |
||
1480 | |||
1481 | // Any function to call? |
||
1482 | if (isset($settings['catch_action']['function'])) |
||
1483 | { |
||
1484 | $hook = $settings['catch_action']['function']; |
||
1485 | |||
1486 | if (!isset($settings['catch_action']['filename'])) |
||
1487 | $settings['catch_action']['filename'] = ''; |
||
1488 | |||
1489 | add_integration_function('integrate_wrap_action', $hook, false, $settings['catch_action']['filename'], false); |
||
1490 | call_integration_hook('integrate_wrap_action'); |
||
1491 | } |
||
1492 | // And finally, the main sub template ;). |
||
1493 | if (isset($settings['catch_action']['sub_template'])) |
||
1494 | $context['sub_template'] = $settings['catch_action']['sub_template']; |
||
1495 | } |
||
1496 | |||
1497 | /** |
||
1498 | * Set an option via javascript. |
||
1499 | * - sets a theme option without outputting anything. |
||
1500 | * - can be used with javascript, via a dummy image... (which doesn't require |
||
1501 | * the page to reload.) |
||
1502 | * - requires someone who is logged in. |
||
1503 | * - accessed via ?action=jsoption;var=variable;val=value;session_var=sess_id. |
||
1504 | * - does not log access to the Who's Online log. (in index.php..) |
||
1505 | */ |
||
1506 | function SetJavaScript() |
||
1507 | { |
||
1508 | global $settings, $user_info, $smcFunc, $options; |
||
1509 | |||
1510 | // Check the session id. |
||
1511 | checkSession('get'); |
||
1512 | |||
1513 | // This good-for-nothing pixel is being used to keep the session alive. |
||
1514 | if (empty($_GET['var']) || !isset($_GET['val'])) |
||
1515 | redirectexit($settings['images_url'] . '/blank.png'); |
||
1516 | |||
1517 | // Sorry, guests can't go any further than this. |
||
1518 | if ($user_info['is_guest'] || $user_info['id'] == 0) |
||
1519 | obExit(false); |
||
1520 | |||
1521 | $reservedVars = array( |
||
1522 | 'actual_theme_url', |
||
1523 | 'actual_images_url', |
||
1524 | 'base_theme_dir', |
||
1525 | 'base_theme_url', |
||
1526 | 'default_images_url', |
||
1527 | 'default_theme_dir', |
||
1528 | 'default_theme_url', |
||
1529 | 'default_template', |
||
1530 | 'images_url', |
||
1531 | 'number_recent_posts', |
||
1532 | 'smiley_sets_default', |
||
1533 | 'theme_dir', |
||
1534 | 'theme_id', |
||
1535 | 'theme_layers', |
||
1536 | 'theme_templates', |
||
1537 | 'theme_url', |
||
1538 | 'name', |
||
1539 | ); |
||
1540 | |||
1541 | // Can't change reserved vars. |
||
1542 | if (in_array(strtolower($_GET['var']), $reservedVars)) |
||
1543 | redirectexit($settings['images_url'] . '/blank.png'); |
||
1544 | |||
1545 | // Use a specific theme? |
||
1546 | if (isset($_GET['th']) || isset($_GET['id'])) |
||
1547 | { |
||
1548 | // Invalidate the current themes cache too. |
||
1549 | cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60); |
||
1550 | |||
1551 | $settings['theme_id'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; |
||
1552 | } |
||
1553 | |||
1554 | // If this is the admin preferences the passed value will just be an element of it. |
||
1555 | if ($_GET['var'] == 'admin_preferences') |
||
1556 | { |
||
1557 | $options['admin_preferences'] = !empty($options['admin_preferences']) ? $smcFunc['json_decode']($options['admin_preferences'], true) : array(); |
||
1558 | // New thingy... |
||
1559 | if (isset($_GET['admin_key']) && strlen($_GET['admin_key']) < 5) |
||
1560 | $options['admin_preferences'][$_GET['admin_key']] = $_GET['val']; |
||
1561 | |||
1562 | // Change the value to be something nice, |
||
1563 | $_GET['val'] = $smcFunc['json_encode']($options['admin_preferences']); |
||
1564 | } |
||
1565 | |||
1566 | // Update the option. |
||
1567 | $smcFunc['db_insert']('replace', |
||
1568 | '{db_prefix}themes', |
||
1569 | array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'), |
||
1570 | array($settings['theme_id'], $user_info['id'], $_GET['var'], is_array($_GET['val']) ? implode(',', $_GET['val']) : $_GET['val']), |
||
1571 | array('id_theme', 'id_member', 'variable') |
||
1572 | ); |
||
1573 | |||
1574 | cache_put_data('theme_settings-' . $settings['theme_id'] . ':' . $user_info['id'], null, 60); |
||
1575 | |||
1576 | // Don't output anything... |
||
1577 | redirectexit($settings['images_url'] . '/blank.png'); |
||
1578 | } |
||
1579 | |||
1580 | /** |
||
1581 | * Shows an interface for editing the templates. |
||
1582 | * - uses the Themes template and edit_template/edit_style sub template. |
||
1583 | * - accessed via ?action=admin;area=theme;sa=edit |
||
1584 | */ |
||
1585 | function EditTheme() |
||
1586 | { |
||
1587 | global $context, $scripturl, $boarddir, $smcFunc, $txt; |
||
1588 | |||
1589 | // @todo Should this be removed? |
||
1590 | if (isset($_REQUEST['preview'])) |
||
1591 | die('die() with fire'); |
||
0 ignored issues
–
show
|
|||
1592 | |||
1593 | isAllowedTo('admin_forum'); |
||
1594 | loadTemplate('Themes'); |
||
1595 | |||
1596 | $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) @$_GET['id']; |
||
1597 | |||
1598 | if (empty($_GET['th'])) |
||
1599 | { |
||
1600 | get_installed_themes(); |
||
1601 | |||
1602 | foreach ($context['themes'] as $key => $theme) |
||
1603 | { |
||
1604 | // There has to be a Settings template! |
||
1605 | if (!file_exists($theme['theme_dir'] . '/index.template.php') && !file_exists($theme['theme_dir'] . '/css/index.css')) |
||
1606 | unset($context['themes'][$key]); |
||
1607 | |||
1608 | else |
||
1609 | $context['themes'][$key]['can_edit_style'] = file_exists($theme['theme_dir'] . '/css/index.css'); |
||
1610 | } |
||
1611 | |||
1612 | $context['sub_template'] = 'edit_list'; |
||
1613 | |||
1614 | return 'no_themes'; |
||
1615 | } |
||
1616 | |||
1617 | $context['session_error'] = false; |
||
1618 | |||
1619 | // Get the directory of the theme we are editing. |
||
1620 | $currentTheme = get_single_theme($_GET['th']); |
||
1621 | $context['theme_id'] = $currentTheme['id']; |
||
1622 | $context['browse_title'] = sprintf($txt['themeadmin_browsing_theme'], $currentTheme['name']); |
||
1623 | |||
1624 | if (!file_exists($currentTheme['theme_dir'] . '/index.template.php') && !file_exists($currentTheme['theme_dir'] . '/css/index.css')) |
||
1625 | fatal_lang_error('theme_edit_missing', false); |
||
1626 | |||
1627 | if (!isset($_REQUEST['filename'])) |
||
1628 | { |
||
1629 | if (isset($_GET['directory'])) |
||
1630 | { |
||
1631 | if (substr($_GET['directory'], 0, 1) == '.') |
||
1632 | $_GET['directory'] = ''; |
||
1633 | else |
||
1634 | { |
||
1635 | $_GET['directory'] = preg_replace(array('~^[\./\\:\0\n\r]+~', '~[\\\\]~', '~/[\./]+~'), array('', '/', '/'), $_GET['directory']); |
||
1636 | |||
1637 | $temp = realpath($currentTheme['theme_dir'] . '/' . $_GET['directory']); |
||
1638 | if (empty($temp) || substr($temp, 0, strlen(realpath($currentTheme['theme_dir']))) != realpath($currentTheme['theme_dir'])) |
||
1639 | $_GET['directory'] = ''; |
||
1640 | } |
||
1641 | } |
||
1642 | |||
1643 | if (isset($_GET['directory']) && $_GET['directory'] != '') |
||
1644 | { |
||
1645 | $context['theme_files'] = get_file_listing($currentTheme['theme_dir'] . '/' . $_GET['directory'], $_GET['directory'] . '/'); |
||
1646 | |||
1647 | $temp = dirname($_GET['directory']); |
||
1648 | array_unshift($context['theme_files'], array( |
||
1649 | 'filename' => $temp == '.' || $temp == '' ? '/ (..)' : $temp . ' (..)', |
||
1650 | 'is_writable' => is_writable($currentTheme['theme_dir'] . '/' . $temp), |
||
1651 | 'is_directory' => true, |
||
1652 | 'is_template' => false, |
||
1653 | 'is_image' => false, |
||
1654 | 'is_editable' => false, |
||
1655 | 'href' => $scripturl . '?action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;directory=' . $temp, |
||
1656 | 'size' => '', |
||
1657 | )); |
||
1658 | } |
||
1659 | else |
||
1660 | $context['theme_files'] = get_file_listing($currentTheme['theme_dir'], ''); |
||
1661 | |||
1662 | // Do not list minified_ files |
||
1663 | foreach ($context['theme_files'] as $key => $file) |
||
1664 | { |
||
1665 | if (strpos($file['filename'], 'minified_') !== false) |
||
1666 | unset($context['theme_files'][$key]); |
||
1667 | } |
||
1668 | |||
1669 | $context['sub_template'] = 'edit_browse'; |
||
1670 | |||
1671 | return; |
||
1672 | } |
||
1673 | else |
||
1674 | { |
||
1675 | if (substr($_REQUEST['filename'], 0, 1) == '.') |
||
1676 | $_REQUEST['filename'] = ''; |
||
1677 | else |
||
1678 | { |
||
1679 | $_REQUEST['filename'] = preg_replace(array('~^[\./\\:\0\n\r]+~', '~[\\\\]~', '~/[\./]+~'), array('', '/', '/'), $_REQUEST['filename']); |
||
1680 | |||
1681 | $temp = realpath($currentTheme['theme_dir'] . '/' . $_REQUEST['filename']); |
||
1682 | if (empty($temp) || substr($temp, 0, strlen(realpath($currentTheme['theme_dir']))) != realpath($currentTheme['theme_dir'])) |
||
1683 | $_REQUEST['filename'] = ''; |
||
1684 | } |
||
1685 | |||
1686 | if (empty($_REQUEST['filename'])) |
||
1687 | fatal_lang_error('theme_edit_missing', false); |
||
1688 | } |
||
1689 | |||
1690 | if (isset($_POST['save'])) |
||
1691 | { |
||
1692 | if (checkSession('post', '', false) == '' && validateToken('admin-te-' . md5($_GET['th'] . '-' . $_REQUEST['filename']), 'post', false) == true) |
||
1693 | { |
||
1694 | if (is_array($_POST['entire_file'])) |
||
1695 | $_POST['entire_file'] = implode("\n", $_POST['entire_file']); |
||
1696 | |||
1697 | $_POST['entire_file'] = rtrim(strtr($_POST['entire_file'], array("\r" => '', ' ' => "\t"))); |
||
1698 | |||
1699 | // Check for a parse error! |
||
1700 | if (substr($_REQUEST['filename'], -13) == '.template.php' && is_writable($currentTheme['theme_dir']) && ini_get('display_errors')) |
||
1701 | { |
||
1702 | $fp = fopen($currentTheme['theme_dir'] . '/tmp_' . session_id() . '.php', 'w'); |
||
1703 | fwrite($fp, $_POST['entire_file']); |
||
1704 | fclose($fp); |
||
1705 | |||
1706 | $error = @file_get_contents($currentTheme['theme_url'] . '/tmp_' . session_id() . '.php'); |
||
1707 | if (preg_match('~ <b>(\d+)</b><br( /)?' . '>$~i', $error) != 0) |
||
1708 | $error_file = $currentTheme['theme_dir'] . '/tmp_' . session_id() . '.php'; |
||
1709 | else |
||
1710 | unlink($currentTheme['theme_dir'] . '/tmp_' . session_id() . '.php'); |
||
1711 | } |
||
1712 | |||
1713 | if (!isset($error_file)) |
||
1714 | { |
||
1715 | $fp = fopen($currentTheme['theme_dir'] . '/' . $_REQUEST['filename'], 'w'); |
||
1716 | fwrite($fp, $_POST['entire_file']); |
||
1717 | fclose($fp); |
||
1718 | |||
1719 | // Nuke any minified files and update $modSettings['browser_cache'] |
||
1720 | deleteAllMinified(); |
||
1721 | |||
1722 | redirectexit('action=admin;area=theme;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=edit;directory=' . dirname($_REQUEST['filename'])); |
||
1723 | } |
||
1724 | } |
||
1725 | // Session timed out. |
||
1726 | else |
||
1727 | { |
||
1728 | loadLanguage('Errors'); |
||
1729 | |||
1730 | $context['session_error'] = true; |
||
1731 | $context['sub_template'] = 'edit_file'; |
||
1732 | |||
1733 | // Recycle the submitted data. |
||
1734 | if (is_array($_POST['entire_file'])) |
||
1735 | $context['entire_file'] = $smcFunc['htmlspecialchars'](implode("\n", $_POST['entire_file'])); |
||
1736 | else |
||
1737 | $context['entire_file'] = $smcFunc['htmlspecialchars']($_POST['entire_file']); |
||
1738 | |||
1739 | $context['edit_filename'] = $smcFunc['htmlspecialchars']($_POST['filename']); |
||
1740 | |||
1741 | // You were able to submit it, so it's reasonable to assume you are allowed to save. |
||
1742 | $context['allow_save'] = true; |
||
1743 | |||
1744 | // Re-create the token so that it can be used |
||
1745 | createToken('admin-te-' . md5($_GET['th'] . '-' . $_REQUEST['filename'])); |
||
1746 | |||
1747 | return; |
||
1748 | } |
||
1749 | } |
||
1750 | |||
1751 | $context['allow_save'] = is_writable($currentTheme['theme_dir'] . '/' . $_REQUEST['filename']); |
||
1752 | $context['allow_save_filename'] = strtr($currentTheme['theme_dir'] . '/' . $_REQUEST['filename'], array($boarddir => '...')); |
||
1753 | $context['edit_filename'] = $smcFunc['htmlspecialchars']($_REQUEST['filename']); |
||
1754 | |||
1755 | if (substr($_REQUEST['filename'], -4) == '.css') |
||
1756 | { |
||
1757 | $context['sub_template'] = 'edit_style'; |
||
1758 | |||
1759 | $context['entire_file'] = $smcFunc['htmlspecialchars'](strtr(file_get_contents($currentTheme['theme_dir'] . '/' . $_REQUEST['filename']), array("\t" => ' '))); |
||
1760 | } |
||
1761 | elseif (substr($_REQUEST['filename'], -13) == '.template.php') |
||
1762 | { |
||
1763 | $context['sub_template'] = 'edit_template'; |
||
1764 | |||
1765 | if (!isset($error_file)) |
||
1766 | $file_data = file($currentTheme['theme_dir'] . '/' . $_REQUEST['filename']); |
||
1767 | else |
||
1768 | { |
||
1769 | if (preg_match('~(<b>.+?</b>:.+?<b>).+?(</b>.+?<b>\d+</b>)<br( /)?' . '>$~i', $error, $match) != 0) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
1770 | $context['parse_error'] = $match[1] . $_REQUEST['filename'] . $match[2]; |
||
1771 | $file_data = file($error_file); |
||
1772 | unlink($error_file); |
||
1773 | } |
||
1774 | |||
1775 | $j = 0; |
||
1776 | $context['file_parts'] = array(array('lines' => 0, 'line' => 1, 'data' => '')); |
||
1777 | for ($i = 0, $n = count($file_data); $i < $n; $i++) |
||
1778 | { |
||
1779 | if (isset($file_data[$i + 1]) && substr($file_data[$i + 1], 0, 9) == 'function ') |
||
1780 | { |
||
1781 | // Try to format the functions a little nicer... |
||
1782 | $context['file_parts'][$j]['data'] = trim($context['file_parts'][$j]['data']) . "\n"; |
||
1783 | |||
1784 | if (empty($context['file_parts'][$j]['lines'])) |
||
1785 | unset($context['file_parts'][$j]); |
||
1786 | $context['file_parts'][++$j] = array('lines' => 0, 'line' => $i + 1, 'data' => ''); |
||
1787 | } |
||
1788 | |||
1789 | $context['file_parts'][$j]['lines']++; |
||
1790 | $context['file_parts'][$j]['data'] .= $smcFunc['htmlspecialchars'](strtr($file_data[$i], array("\t" => ' '))); |
||
1791 | } |
||
1792 | |||
1793 | $context['entire_file'] = $smcFunc['htmlspecialchars'](strtr(implode('', $file_data), array("\t" => ' '))); |
||
1794 | } |
||
1795 | else |
||
1796 | { |
||
1797 | $context['sub_template'] = 'edit_file'; |
||
1798 | |||
1799 | $context['entire_file'] = $smcFunc['htmlspecialchars'](strtr(file_get_contents($currentTheme['theme_dir'] . '/' . $_REQUEST['filename']), array("\t" => ' '))); |
||
1800 | } |
||
1801 | |||
1802 | // Create a special token to allow editing of multiple files. |
||
1803 | createToken('admin-te-' . md5($_GET['th'] . '-' . $_REQUEST['filename'])); |
||
1804 | } |
||
1805 | |||
1806 | /** |
||
1807 | * Makes a copy of a template file in a new location |
||
1808 | * |
||
1809 | * @uses template_copy_template() |
||
1810 | */ |
||
1811 | function CopyTemplate() |
||
1812 | { |
||
1813 | global $context, $settings; |
||
1814 | |||
1815 | isAllowedTo('admin_forum'); |
||
1816 | loadTemplate('Themes'); |
||
1817 | |||
1818 | $context[$context['admin_menu_name']]['current_subsection'] = 'edit'; |
||
1819 | |||
1820 | $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id']; |
||
1821 | |||
1822 | if (empty($_GET['th'])) |
||
1823 | fatal_lang_error('theme_install_invalid_id'); |
||
1824 | |||
1825 | // Get the theme info. |
||
1826 | $theme = get_single_theme($_GET['th']); |
||
1827 | $context['theme_id'] = $theme['id']; |
||
1828 | |||
1829 | if (isset($_REQUEST['template']) && preg_match('~[\./\\\\:\0]~', $_REQUEST['template']) == 0) |
||
1830 | { |
||
1831 | if (file_exists($settings['default_theme_dir'] . '/' . $_REQUEST['template'] . '.template.php')) |
||
1832 | $filename = $settings['default_theme_dir'] . '/' . $_REQUEST['template'] . '.template.php'; |
||
1833 | |||
1834 | else |
||
1835 | fatal_lang_error('no_access', false); |
||
1836 | |||
1837 | $fp = fopen($theme['theme_dir'] . '/' . $_REQUEST['template'] . '.template.php', 'w'); |
||
1838 | fwrite($fp, file_get_contents($filename)); |
||
1839 | fclose($fp); |
||
1840 | |||
1841 | redirectexit('action=admin;area=theme;th=' . $context['theme_id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=copy'); |
||
1842 | } |
||
1843 | elseif (isset($_REQUEST['lang_file']) && preg_match('~^[^\./\\\\:\0]\.[^\./\\\\:\0]$~', $_REQUEST['lang_file']) != 0) |
||
1844 | { |
||
1845 | if (file_exists($settings['default_theme_dir'] . '/languages/' . $_REQUEST['template'] . '.php')) |
||
1846 | $filename = $settings['default_theme_dir'] . '/languages/' . $_REQUEST['template'] . '.php'; |
||
1847 | |||
1848 | else |
||
1849 | fatal_lang_error('no_access', false); |
||
1850 | |||
1851 | $fp = fopen($theme['theme_dir'] . '/languages/' . $_REQUEST['lang_file'] . '.php', 'w'); |
||
1852 | fwrite($fp, file_get_contents($filename)); |
||
1853 | fclose($fp); |
||
1854 | |||
1855 | redirectexit('action=admin;area=theme;th=' . $context['theme_id'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';sa=copy'); |
||
1856 | } |
||
1857 | |||
1858 | $templates = array(); |
||
1859 | $lang_files = array(); |
||
1860 | |||
1861 | $dir = dir($settings['default_theme_dir']); |
||
1862 | while ($entry = $dir->read()) |
||
1863 | { |
||
1864 | if (substr($entry, -13) == '.template.php') |
||
1865 | $templates[] = substr($entry, 0, -13); |
||
1866 | } |
||
1867 | $dir->close(); |
||
1868 | |||
1869 | $dir = dir($settings['default_theme_dir'] . '/languages'); |
||
1870 | while ($entry = $dir->read()) |
||
1871 | { |
||
1872 | if (preg_match('~^([^\.]+\.[^\.]+)\.php$~', $entry, $matches)) |
||
1873 | $lang_files[] = $matches[1]; |
||
1874 | } |
||
1875 | $dir->close(); |
||
1876 | |||
1877 | natcasesort($templates); |
||
1878 | natcasesort($lang_files); |
||
1879 | |||
1880 | $context['available_templates'] = array(); |
||
1881 | foreach ($templates as $template) |
||
1882 | $context['available_templates'][$template] = array( |
||
1883 | 'filename' => $template . '.template.php', |
||
1884 | 'value' => $template, |
||
1885 | 'already_exists' => false, |
||
1886 | 'can_copy' => is_writable($theme['theme_dir']), |
||
1887 | ); |
||
1888 | $context['available_language_files'] = array(); |
||
1889 | foreach ($lang_files as $file) |
||
1890 | $context['available_language_files'][$file] = array( |
||
1891 | 'filename' => $file . '.php', |
||
1892 | 'value' => $file, |
||
1893 | 'already_exists' => false, |
||
1894 | 'can_copy' => file_exists($theme['theme_dir'] . '/languages') ? is_writable($theme['theme_dir'] . '/languages') : is_writable($theme['theme_dir']), |
||
1895 | ); |
||
1896 | |||
1897 | $dir = dir($theme['theme_dir']); |
||
1898 | while ($entry = $dir->read()) |
||
1899 | { |
||
1900 | if (substr($entry, -13) == '.template.php' && isset($context['available_templates'][substr($entry, 0, -13)])) |
||
1901 | { |
||
1902 | $context['available_templates'][substr($entry, 0, -13)]['already_exists'] = true; |
||
1903 | $context['available_templates'][substr($entry, 0, -13)]['can_copy'] = is_writable($theme['theme_dir'] . '/' . $entry); |
||
1904 | } |
||
1905 | } |
||
1906 | $dir->close(); |
||
1907 | |||
1908 | if (file_exists($theme['theme_dir'] . '/languages')) |
||
1909 | { |
||
1910 | $dir = dir($theme['theme_dir'] . '/languages'); |
||
1911 | while ($entry = $dir->read()) |
||
1912 | { |
||
1913 | if (preg_match('~^([^\.]+\.[^\.]+)\.php$~', $entry, $matches) && isset($context['available_language_files'][$matches[1]])) |
||
1914 | { |
||
1915 | $context['available_language_files'][$matches[1]]['already_exists'] = true; |
||
1916 | $context['available_language_files'][$matches[1]]['can_copy'] = is_writable($theme['theme_dir'] . '/languages/' . $entry); |
||
1917 | } |
||
1918 | } |
||
1919 | $dir->close(); |
||
1920 | } |
||
1921 | |||
1922 | $context['sub_template'] = 'copy_template'; |
||
1923 | } |
||
1924 | |||
1925 | ?> |