comparison forum/Sources/Themes.php @ 76:e3e11437ecea website

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