PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/Sources/Themes.php

https://github.com/ArsenArsen/SMF2.1
PHP | 1973 lines | 1388 code | 311 blank | 274 comment | 265 complexity | 032ff75de159b53c74dc82d55f73e125 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * This file concerns itself almost completely with theme administration.
  4. * Its tasks include changing theme settings, installing and removing
  5. * themes, choosing the current theme, and editing themes.
  6. *
  7. * @todo Update this for the new package manager?
  8. *
  9. * Creating and distributing theme packages:
  10. * There isn't that much required to package and distribute your own themes...
  11. * just do the following:
  12. * - create a theme_info.xml file, with the root element theme-info.
  13. * - its name should go in a name element, just like description.
  14. * - your name should go in author. (email in the email attribute.)
  15. * - any support website for the theme should be in website.
  16. * - layers and templates (non-default) should go in those elements ;).
  17. * - if the images dir isn't images, specify in the images element.
  18. * - any extra rows for themes should go in extra, serialized. (as in array(variable => value).)
  19. * - tar and gzip the directory - and you're done!
  20. * - please include any special license in a license.txt file.
  21. *
  22. * Simple Machines Forum (SMF)
  23. *
  24. * @package SMF
  25. * @author Simple Machines http://www.simplemachines.org
  26. * @copyright 2014 Simple Machines and individual contributors
  27. * @license http://www.simplemachines.org/about/smf/license.php BSD
  28. *
  29. * @version 2.1 Alpha 1
  30. */
  31. if (!defined('SMF'))
  32. die('No direct access...');
  33. /**
  34. * Subaction handler - manages the action and delegates control to the proper
  35. * sub-action.
  36. * It loads both the Themes and Settings language files.
  37. * Checks the session by GET or POST to verify the sent data.
  38. * Requires the user not be a guest. (@todo what?)
  39. * Accessed via ?action=admin;area=theme.
  40. */
  41. function ThemesMain()
  42. {
  43. global $txt, $context, $sourcedir;
  44. // Load the important language files...
  45. loadLanguage('Themes');
  46. loadLanguage('Settings');
  47. loadLanguage('Drafts');
  48. // No funny business - guests only.
  49. is_not_guest();
  50. require_once($sourcedir . '/Subs-Themes.php');
  51. // Default the page title to Theme Administration by default.
  52. $context['page_title'] = $txt['themeadmin_title'];
  53. // Theme administration, removal, choice, or installation...
  54. $subActions = array(
  55. 'admin' => 'ThemeAdmin',
  56. 'list' => 'ThemeList',
  57. 'reset' => 'SetThemeOptions',
  58. 'options' => 'SetThemeOptions',
  59. 'install' => 'ThemeInstall',
  60. 'remove' => 'RemoveTheme',
  61. 'pick' => 'PickTheme',
  62. 'edit' => 'EditTheme',
  63. 'enable' => 'EnableTheme',
  64. 'copy' => 'CopyTemplate',
  65. );
  66. // @todo Layout Settings? huh?
  67. if (!empty($context['admin_menu_name']))
  68. {
  69. $context[$context['admin_menu_name']]['tab_data'] = array(
  70. 'title' => $txt['themeadmin_title'],
  71. 'help' => 'themes',
  72. 'description' => $txt['themeadmin_description'],
  73. 'tabs' => array(
  74. 'admin' => array(
  75. 'description' => $txt['themeadmin_admin_desc'],
  76. ),
  77. 'list' => array(
  78. 'description' => $txt['themeadmin_list_desc'],
  79. ),
  80. 'reset' => array(
  81. 'description' => $txt['themeadmin_reset_desc'],
  82. ),
  83. 'edit' => array(
  84. 'description' => $txt['themeadmin_edit_desc'],
  85. ),
  86. ),
  87. );
  88. }
  89. // CRUD $subActions as needed.
  90. call_integration_hook('integrate_manage_themes', array(&$subActions));
  91. // Follow the sa or just go to administration.
  92. if (isset($_GET['sa']) && !empty($subActions[$_GET['sa']]))
  93. call_helper($subActions[$_GET['sa']]);
  94. else
  95. call_helper($subActions['admin']);
  96. }
  97. /**
  98. * This function allows administration of themes and their settings,
  99. * as well as global theme settings.
  100. * - sets the settings theme_allow, theme_guests, and knownThemes.
  101. * - requires the admin_forum permission.
  102. * - accessed with ?action=admin;area=theme;sa=admin.
  103. *
  104. * @uses Themes template
  105. * @uses Admin language file
  106. */
  107. function ThemeAdmin()
  108. {
  109. global $context, $boarddir;
  110. // Are handling any settings?
  111. if (isset($_POST['save']))
  112. {
  113. checkSession();
  114. validateToken('admin-tm');
  115. if (isset($_POST['options']['known_themes']))
  116. foreach ($_POST['options']['known_themes'] as $key => $id)
  117. $_POST['options']['known_themes'][$key] = (int) $id;
  118. else
  119. fatal_lang_error('themes_none_selectable', false);
  120. if (!in_array($_POST['options']['theme_guests'], $_POST['options']['known_themes']))
  121. fatal_lang_error('themes_default_selectable', false);
  122. // Commit the new settings.
  123. updateSettings(array(
  124. 'theme_allow' => $_POST['options']['theme_allow'],
  125. 'theme_guests' => $_POST['options']['theme_guests'],
  126. 'knownThemes' => implode(',', $_POST['options']['known_themes']),
  127. ));
  128. if ((int) $_POST['theme_reset'] == 0 || in_array($_POST['theme_reset'], $_POST['options']['known_themes']))
  129. updateMemberData(null, array('id_theme' => (int) $_POST['theme_reset']));
  130. redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=admin');
  131. }
  132. loadLanguage('Admin');
  133. isAllowedTo('admin_forum');
  134. loadTemplate('Themes');
  135. // List all installed and enabled themes.
  136. get_all_themes(true);
  137. // Can we create a new theme?
  138. $context['can_create_new'] = is_writable($boarddir . '/Themes');
  139. $context['new_theme_dir'] = substr(realpath($boarddir . '/Themes/default'), 0, -7);
  140. // Look for a non existent theme directory. (ie theme87.)
  141. $theme_dir = $boarddir . '/Themes/theme';
  142. $i = 1;
  143. while (file_exists($theme_dir . $i))
  144. $i++;
  145. $context['new_theme_name'] = 'theme' . $i;
  146. // A bunch of tokens for a bunch of forms.
  147. createToken('admin-tm');
  148. createToken('admin-t-file');
  149. createToken('admin-t-copy');
  150. createToken('admin-t-dir');
  151. }
  152. /**
  153. * This function lists the available themes and provides an interface to reset
  154. * the paths of all the installed themes.
  155. */
  156. function ThemeList()
  157. {
  158. global $context, $boarddir, $boardurl, $smcFunc;
  159. loadLanguage('Admin');
  160. isAllowedTo('admin_forum');
  161. if (isset($_REQUEST['th']))
  162. return SetThemeSettings();
  163. if (isset($_POST['save']))
  164. {
  165. checkSession();
  166. validateToken('admin-tl');
  167. // Calling the almighty power of global vars!
  168. get_all_themes(false);
  169. $setValues = array();
  170. foreach ($context['themes'] as $id => $theme)
  171. {
  172. if (file_exists($_POST['reset_dir'] . '/' . basename($theme['theme_dir'])))
  173. {
  174. $setValues[] = array($id, 0, 'theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['theme_dir'])));
  175. $setValues[] = array($id, 0, 'theme_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir']));
  176. $setValues[] = array($id, 0, 'images_url', $_POST['reset_url'] . '/' . basename($theme['theme_dir']) . '/' . basename($theme['images_url']));
  177. }
  178. if (isset($theme['base_theme_dir']) && file_exists($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir'])))
  179. {
  180. $setValues[] = array($id, 0, 'base_theme_dir', realpath($_POST['reset_dir'] . '/' . basename($theme['base_theme_dir'])));
  181. $setValues[] = array($id, 0, 'base_theme_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir']));
  182. $setValues[] = array($id, 0, 'base_images_url', $_POST['reset_url'] . '/' . basename($theme['base_theme_dir']) . '/' . basename($theme['base_images_url']));
  183. }
  184. cache_put_data('theme_settings-' . $id, null, 90);
  185. }
  186. if (!empty($setValues))
  187. {
  188. $smcFunc['db_insert']('replace',
  189. '{db_prefix}themes',
  190. array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  191. $setValues,
  192. array('id_theme', 'variable', 'id_member')
  193. );
  194. }
  195. redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id']);
  196. }
  197. loadTemplate('Themes');
  198. // Get all installed themes.
  199. get_all_themes(false);
  200. $context['reset_dir'] = realpath($boarddir . '/Themes');
  201. $context['reset_url'] = $boardurl . '/Themes';
  202. $context['sub_template'] = 'list_themes';
  203. createToken('admin-tl');
  204. createToken('admin-tr', 'request');
  205. createToken('admin-tre', 'request');
  206. }
  207. /**
  208. * Administrative global settings.
  209. */
  210. function SetThemeOptions()
  211. {
  212. global $txt, $context, $settings, $modSettings, $smcFunc;
  213. $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (isset($_GET['id']) ? (int) $_GET['id'] : 0);
  214. isAllowedTo('admin_forum');
  215. if (empty($_GET['th']) && empty($_GET['id']))
  216. {
  217. $request = $smcFunc['db_query']('', '
  218. SELECT id_theme, variable, value
  219. FROM {db_prefix}themes
  220. WHERE variable IN ({string:name}, {string:theme_dir})
  221. AND id_member = {int:no_member}',
  222. array(
  223. 'no_member' => 0,
  224. 'name' => 'name',
  225. 'theme_dir' => 'theme_dir',
  226. )
  227. );
  228. $context['themes'] = array();
  229. while ($row = $smcFunc['db_fetch_assoc']($request))
  230. {
  231. if (!isset($context['themes'][$row['id_theme']]))
  232. $context['themes'][$row['id_theme']] = array(
  233. 'id' => $row['id_theme'],
  234. 'num_default_options' => 0,
  235. 'num_members' => 0,
  236. );
  237. $context['themes'][$row['id_theme']][$row['variable']] = $row['value'];
  238. }
  239. $smcFunc['db_free_result']($request);
  240. $request = $smcFunc['db_query']('', '
  241. SELECT id_theme, COUNT(*) AS value
  242. FROM {db_prefix}themes
  243. WHERE id_member = {int:guest_member}
  244. GROUP BY id_theme',
  245. array(
  246. 'guest_member' => -1,
  247. )
  248. );
  249. while ($row = $smcFunc['db_fetch_assoc']($request))
  250. $context['themes'][$row['id_theme']]['num_default_options'] = $row['value'];
  251. $smcFunc['db_free_result']($request);
  252. // Need to make sure we don't do custom fields.
  253. $request = $smcFunc['db_query']('', '
  254. SELECT col_name
  255. FROM {db_prefix}custom_fields',
  256. array(
  257. )
  258. );
  259. $customFields = array();
  260. while ($row = $smcFunc['db_fetch_assoc']($request))
  261. $customFields[] = $row['col_name'];
  262. $smcFunc['db_free_result']($request);
  263. $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})');
  264. $request = $smcFunc['db_query']('themes_count', '
  265. SELECT COUNT(DISTINCT id_member) AS value, id_theme
  266. FROM {db_prefix}themes
  267. WHERE id_member > {int:no_member}
  268. ' . $customFieldsQuery . '
  269. GROUP BY id_theme',
  270. array(
  271. 'no_member' => 0,
  272. 'custom_fields' => empty($customFields) ? array() : $customFields,
  273. )
  274. );
  275. while ($row = $smcFunc['db_fetch_assoc']($request))
  276. $context['themes'][$row['id_theme']]['num_members'] = $row['value'];
  277. $smcFunc['db_free_result']($request);
  278. // There has to be a Settings template!
  279. foreach ($context['themes'] as $k => $v)
  280. if (empty($v['theme_dir']) || (!file_exists($v['theme_dir'] . '/Settings.template.php') && empty($v['num_members'])))
  281. unset($context['themes'][$k]);
  282. loadTemplate('Themes');
  283. $context['sub_template'] = 'reset_list';
  284. createToken('admin-stor', 'request');
  285. return;
  286. }
  287. // Submit?
  288. if (isset($_POST['submit']) && empty($_POST['who']))
  289. {
  290. checkSession();
  291. validateToken('admin-sto');
  292. if (empty($_POST['options']))
  293. $_POST['options'] = array();
  294. if (empty($_POST['default_options']))
  295. $_POST['default_options'] = array();
  296. // Set up the sql query.
  297. $setValues = array();
  298. foreach ($_POST['options'] as $opt => $val)
  299. $setValues[] = array(-1, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val);
  300. $old_settings = array();
  301. foreach ($_POST['default_options'] as $opt => $val)
  302. {
  303. $old_settings[] = $opt;
  304. $setValues[] = array(-1, 1, $opt, is_array($val) ? implode(',', $val) : $val);
  305. }
  306. // If we're actually inserting something..
  307. if (!empty($setValues))
  308. {
  309. // Are there options in non-default themes set that should be cleared?
  310. if (!empty($old_settings))
  311. $smcFunc['db_query']('', '
  312. DELETE FROM {db_prefix}themes
  313. WHERE id_theme != {int:default_theme}
  314. AND id_member = {int:guest_member}
  315. AND variable IN ({array_string:old_settings})',
  316. array(
  317. 'default_theme' => 1,
  318. 'guest_member' => -1,
  319. 'old_settings' => $old_settings,
  320. )
  321. );
  322. $smcFunc['db_insert']('replace',
  323. '{db_prefix}themes',
  324. array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  325. $setValues,
  326. array('id_theme', 'variable', 'id_member')
  327. );
  328. }
  329. cache_put_data('theme_settings-' . $_GET['th'], null, 90);
  330. cache_put_data('theme_settings-1', null, 90);
  331. redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset');
  332. }
  333. elseif (isset($_POST['submit']) && $_POST['who'] == 1)
  334. {
  335. checkSession();
  336. validateToken('admin-sto');
  337. $_POST['options'] = empty($_POST['options']) ? array() : $_POST['options'];
  338. $_POST['options_master'] = empty($_POST['options_master']) ? array() : $_POST['options_master'];
  339. $_POST['default_options'] = empty($_POST['default_options']) ? array() : $_POST['default_options'];
  340. $_POST['default_options_master'] = empty($_POST['default_options_master']) ? array() : $_POST['default_options_master'];
  341. $old_settings = array();
  342. foreach ($_POST['default_options'] as $opt => $val)
  343. {
  344. if ($_POST['default_options_master'][$opt] == 0)
  345. continue;
  346. elseif ($_POST['default_options_master'][$opt] == 1)
  347. {
  348. // Delete then insert for ease of database compatibility!
  349. $smcFunc['db_query']('substring', '
  350. DELETE FROM {db_prefix}themes
  351. WHERE id_theme = {int:default_theme}
  352. AND id_member != {int:no_member}
  353. AND variable = SUBSTRING({string:option}, 1, 255)',
  354. array(
  355. 'default_theme' => 1,
  356. 'no_member' => 0,
  357. 'option' => $opt,
  358. )
  359. );
  360. $smcFunc['db_query']('substring', '
  361. INSERT INTO {db_prefix}themes
  362. (id_member, id_theme, variable, value)
  363. SELECT id_member, 1, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534)
  364. FROM {db_prefix}members',
  365. array(
  366. 'option' => $opt,
  367. 'value' => (is_array($val) ? implode(',', $val) : $val),
  368. )
  369. );
  370. $old_settings[] = $opt;
  371. }
  372. elseif ($_POST['default_options_master'][$opt] == 2)
  373. {
  374. $smcFunc['db_query']('', '
  375. DELETE FROM {db_prefix}themes
  376. WHERE variable = {string:option_name}
  377. AND id_member > {int:no_member}',
  378. array(
  379. 'no_member' => 0,
  380. 'option_name' => $opt,
  381. )
  382. );
  383. }
  384. }
  385. // Delete options from other themes.
  386. if (!empty($old_settings))
  387. $smcFunc['db_query']('', '
  388. DELETE FROM {db_prefix}themes
  389. WHERE id_theme != {int:default_theme}
  390. AND id_member > {int:no_member}
  391. AND variable IN ({array_string:old_settings})',
  392. array(
  393. 'default_theme' => 1,
  394. 'no_member' => 0,
  395. 'old_settings' => $old_settings,
  396. )
  397. );
  398. foreach ($_POST['options'] as $opt => $val)
  399. {
  400. if ($_POST['options_master'][$opt] == 0)
  401. continue;
  402. elseif ($_POST['options_master'][$opt] == 1)
  403. {
  404. // Delete then insert for ease of database compatibility - again!
  405. $smcFunc['db_query']('substring', '
  406. DELETE FROM {db_prefix}themes
  407. WHERE id_theme = {int:current_theme}
  408. AND id_member != {int:no_member}
  409. AND variable = SUBSTRING({string:option}, 1, 255)',
  410. array(
  411. 'current_theme' => $_GET['th'],
  412. 'no_member' => 0,
  413. 'option' => $opt,
  414. )
  415. );
  416. $smcFunc['db_query']('substring', '
  417. INSERT INTO {db_prefix}themes
  418. (id_member, id_theme, variable, value)
  419. SELECT id_member, {int:current_theme}, SUBSTRING({string:option}, 1, 255), SUBSTRING({string:value}, 1, 65534)
  420. FROM {db_prefix}members',
  421. array(
  422. 'current_theme' => $_GET['th'],
  423. 'option' => $opt,
  424. 'value' => (is_array($val) ? implode(',', $val) : $val),
  425. )
  426. );
  427. }
  428. elseif ($_POST['options_master'][$opt] == 2)
  429. {
  430. $smcFunc['db_query']('', '
  431. DELETE FROM {db_prefix}themes
  432. WHERE variable = {string:option}
  433. AND id_member > {int:no_member}
  434. AND id_theme = {int:current_theme}',
  435. array(
  436. 'no_member' => 0,
  437. 'current_theme' => $_GET['th'],
  438. 'option' => $opt,
  439. )
  440. );
  441. }
  442. }
  443. redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset');
  444. }
  445. elseif (!empty($_GET['who']) && $_GET['who'] == 2)
  446. {
  447. checkSession('get');
  448. validateToken('admin-stor', 'request');
  449. // Don't delete custom fields!!
  450. if ($_GET['th'] == 1)
  451. {
  452. $request = $smcFunc['db_query']('', '
  453. SELECT col_name
  454. FROM {db_prefix}custom_fields',
  455. array(
  456. )
  457. );
  458. $customFields = array();
  459. while ($row = $smcFunc['db_fetch_assoc']($request))
  460. $customFields[] = $row['col_name'];
  461. $smcFunc['db_free_result']($request);
  462. }
  463. $customFieldsQuery = empty($customFields) ? '' : ('AND variable NOT IN ({array_string:custom_fields})');
  464. $smcFunc['db_query']('', '
  465. DELETE FROM {db_prefix}themes
  466. WHERE id_member > {int:no_member}
  467. AND id_theme = {int:current_theme}
  468. ' . $customFieldsQuery,
  469. array(
  470. 'no_member' => 0,
  471. 'current_theme' => $_GET['th'],
  472. 'custom_fields' => empty($customFields) ? array() : $customFields,
  473. )
  474. );
  475. redirectexit('action=admin;area=theme;' . $context['session_var'] . '=' . $context['session_id'] . ';sa=reset');
  476. }
  477. $old_id = $settings['theme_id'];
  478. $old_settings = $settings;
  479. loadTheme($_GET['th'], false);
  480. loadLanguage('Profile');
  481. // @todo Should we just move these options so they are no longer theme dependant?
  482. loadLanguage('PersonalMessage');
  483. // Let the theme take care of the settings.
  484. loadTemplate('Settings');
  485. loadSubTemplate('options');
  486. $context['sub_template'] = 'set_options';
  487. $context['page_title'] = $txt['theme_settings'];
  488. $context['options'] = $context['theme_options'];
  489. $context['theme_settings'] = $settings;
  490. if (empty($_REQUEST['who']))
  491. {
  492. $request = $smcFunc['db_query']('', '
  493. SELECT variable, value
  494. FROM {db_prefix}themes
  495. WHERE id_theme IN (1, {int:current_theme})
  496. AND id_member = {int:guest_member}',
  497. array(
  498. 'current_theme' => $_GET['th'],
  499. 'guest_member' => -1,
  500. )
  501. );
  502. $context['theme_options'] = array();
  503. while ($row = $smcFunc['db_fetch_assoc']($request))
  504. $context['theme_options'][$row['variable']] = $row['value'];
  505. $smcFunc['db_free_result']($request);
  506. $context['theme_options_reset'] = false;
  507. }
  508. else
  509. {
  510. $context['theme_options'] = array();
  511. $context['theme_options_reset'] = true;
  512. }
  513. foreach ($context['options'] as $i => $setting)
  514. {
  515. // Is this disabled?
  516. if ($setting['id'] == 'calendar_start_day' && empty($modSettings['cal_enabled']))
  517. {
  518. unset($context['options'][$i]);
  519. continue;
  520. }
  521. elseif (($setting['id'] == 'topics_per_page' || $setting['id'] == 'messages_per_page') && !empty($modSettings['disableCustomPerPage']))
  522. {
  523. unset($context['options'][$i]);
  524. continue;
  525. }
  526. if (!isset($setting['type']) || $setting['type'] == 'bool')
  527. $context['options'][$i]['type'] = 'checkbox';
  528. elseif ($setting['type'] == 'int' || $setting['type'] == 'integer')
  529. $context['options'][$i]['type'] = 'number';
  530. elseif ($setting['type'] == 'string')
  531. $context['options'][$i]['type'] = 'text';
  532. if (isset($setting['options']))
  533. $context['options'][$i]['type'] = 'list';
  534. $context['options'][$i]['value'] = !isset($context['theme_options'][$setting['id']]) ? '' : $context['theme_options'][$setting['id']];
  535. }
  536. // Restore the existing theme.
  537. loadTheme($old_id, false);
  538. $settings = $old_settings;
  539. loadTemplate('Themes');
  540. createToken('admin-sto');
  541. }
  542. /**
  543. * Administrative global settings.
  544. * - saves and requests global theme settings. ($settings)
  545. * - loads the Admin language file.
  546. * - calls ThemeAdmin() if no theme is specified. (the theme center.)
  547. * - requires admin_forum permission.
  548. * - accessed with ?action=admin;area=theme;sa=list&th=xx.
  549. */
  550. function SetThemeSettings()
  551. {
  552. global $txt, $context, $settings, $modSettings, $smcFunc;
  553. if (empty($_GET['th']) && empty($_GET['id']))
  554. return ThemeAdmin();
  555. $_GET['th'] = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id'];
  556. // Select the best fitting tab.
  557. $context[$context['admin_menu_name']]['current_subsection'] = 'list';
  558. loadLanguage('Admin');
  559. isAllowedTo('admin_forum');
  560. // Validate inputs/user.
  561. if (empty($_GET['th']))
  562. fatal_lang_error('no_theme', false);
  563. // Fetch the smiley sets...
  564. $sets = explode(',', 'none,' . $modSettings['smiley_sets_known']);
  565. $set_names = explode("\n", $txt['smileys_none'] . "\n" . $modSettings['smiley_sets_names']);
  566. $context['smiley_sets'] = array(
  567. '' => $txt['smileys_no_default']
  568. );
  569. foreach ($sets as $i => $set)
  570. $context['smiley_sets'][$set] = $smcFunc['htmlspecialchars']($set_names[$i]);
  571. $old_id = $settings['theme_id'];
  572. $old_settings = $settings;
  573. loadTheme($_GET['th'], false);
  574. // Sadly we really do need to init the template.
  575. loadSubTemplate('init', 'ignore');
  576. // Also load the actual themes language file - in case of special settings.
  577. loadLanguage('Settings', '', true, true);
  578. // And the custom language strings...
  579. loadLanguage('ThemeStrings', '', false, true);
  580. // Let the theme take care of the settings.
  581. loadTemplate('Settings');
  582. loadSubTemplate('settings');
  583. // Load the variants separately...
  584. $settings['theme_variants'] = array();
  585. if (file_exists($settings['theme_dir'] . '/index.template.php'))
  586. {
  587. $file_contents = implode('', file($settings['theme_dir'] . '/index.template.php'));
  588. if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches))
  589. eval('global $settings;' . $matches[0]);
  590. }
  591. // Submitting!
  592. if (isset($_POST['save']))
  593. {
  594. checkSession();
  595. validateToken('admin-sts');
  596. if (empty($_POST['options']))
  597. $_POST['options'] = array();
  598. if (empty($_POST['default_options']))
  599. $_POST['default_options'] = array();
  600. // Make sure items are cast correctly.
  601. foreach ($context['theme_settings'] as $item)
  602. {
  603. // Disregard this item if this is just a separator.
  604. if (!is_array($item))
  605. continue;
  606. foreach (array('options', 'default_options') as $option)
  607. {
  608. if (!isset($_POST[$option][$item['id']]))
  609. continue;
  610. // Checkbox.
  611. elseif (empty($item['type']))
  612. $_POST[$option][$item['id']] = $_POST[$option][$item['id']] ? 1 : 0;
  613. // Number
  614. elseif ($item['type'] == 'number')
  615. $_POST[$option][$item['id']] = (int) $_POST[$option][$item['id']];
  616. }
  617. }
  618. // Set up the sql query.
  619. $inserts = array();
  620. foreach ($_POST['options'] as $opt => $val)
  621. $inserts[] = array(0, $_GET['th'], $opt, is_array($val) ? implode(',', $val) : $val);
  622. foreach ($_POST['default_options'] as $opt => $val)
  623. $inserts[] = array(0, 1, $opt, is_array($val) ? implode(',', $val) : $val);
  624. // If we're actually inserting something..
  625. if (!empty($inserts))
  626. {
  627. $smcFunc['db_insert']('replace',
  628. '{db_prefix}themes',
  629. array('id_member' => 'int', 'id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  630. $inserts,
  631. array('id_member', 'id_theme', 'variable')
  632. );
  633. }
  634. cache_put_data('theme_settings-' . $_GET['th'], null, 90);
  635. cache_put_data('theme_settings-1', null, 90);
  636. // Invalidate the cache.
  637. updateSettings(array('settings_updated' => time()));
  638. redirectexit('action=admin;area=theme;sa=list;th=' . $_GET['th'] . ';' . $context['session_var'] . '=' . $context['session_id']);
  639. }
  640. $context['sub_template'] = 'set_settings';
  641. $context['page_title'] = $txt['theme_settings'];
  642. foreach ($settings as $setting => $dummy)
  643. {
  644. if (!in_array($setting, array('theme_url', 'theme_dir', 'images_url', 'template_dirs')))
  645. $settings[$setting] = htmlspecialchars__recursive($settings[$setting]);
  646. }
  647. $context['settings'] = $context['theme_settings'];
  648. $context['theme_settings'] = $settings;
  649. foreach ($context['settings'] as $i => $setting)
  650. {
  651. // Separators are dummies, so leave them alone.
  652. if (!is_array($setting))
  653. continue;
  654. if (!isset($setting['type']) || $setting['type'] == 'bool')
  655. $context['settings'][$i]['type'] = 'checkbox';
  656. elseif ($setting['type'] == 'int' || $setting['type'] == 'integer')
  657. $context['settings'][$i]['type'] = 'number';
  658. elseif ($setting['type'] == 'string')
  659. $context['settings'][$i]['type'] = 'text';
  660. if (isset($setting['options']))
  661. $context['settings'][$i]['type'] = 'list';
  662. $context['settings'][$i]['value'] = !isset($settings[$setting['id']]) ? '' : $settings[$setting['id']];
  663. }
  664. // Do we support variants?
  665. if (!empty($settings['theme_variants']))
  666. {
  667. $context['theme_variants'] = array();
  668. foreach ($settings['theme_variants'] as $variant)
  669. {
  670. // Have any text, old chap?
  671. $context['theme_variants'][$variant] = array(
  672. 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant,
  673. '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'),
  674. );
  675. }
  676. $context['default_variant'] = !empty($settings['default_variant']) && isset($context['theme_variants'][$settings['default_variant']]) ? $settings['default_variant'] : $settings['theme_variants'][0];
  677. }
  678. // Restore the current theme.
  679. loadTheme($old_id, false);
  680. // Reinit just incase.
  681. loadSubTemplate('init', 'ignore');
  682. $settings = $old_settings;
  683. loadTemplate('Themes');
  684. // We like Kenny better than Token.
  685. createToken('admin-sts');
  686. }
  687. /**
  688. * Remove a theme from the database.
  689. * - removes an installed theme.
  690. * - requires an administrator.
  691. * - accessed with ?action=admin;area=theme;sa=remove.
  692. */
  693. function RemoveTheme()
  694. {
  695. global $context;
  696. checkSession('get');
  697. isAllowedTo('admin_forum');
  698. validateToken('admin-tr', 'request');
  699. // The theme's ID must be an integer.
  700. $themeID = isset($_GET['th']) ? (int) $_GET['th'] : (int) $_GET['id'];
  701. // You can't delete the default theme!
  702. if ($themeID == 1)
  703. fatal_lang_error('no_access', false);
  704. $theme_info = get_single_theme($themeID);
  705. // Remove it from the DB.
  706. remove_theme($themeID);
  707. // And remove all its files and folders too.
  708. if (!empty($theme_info) && !empty($theme_info['theme_dir']))
  709. remove_dir($theme_info['theme_dir']);
  710. // Go back to the list page.
  711. redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'] .';done=removing');
  712. }
  713. function EnableTheme()
  714. {
  715. global $modSettings, $context;
  716. checkSession('get');
  717. isAllowedTo('admin_forum');
  718. validateToken('admin-tre', 'request');
  719. // The theme's ID must be an string.
  720. $themeID = isset($_GET['th']) ? (string) trim($_GET['th']) : (string) trim($_GET['id']);
  721. // Get the current list.
  722. $enableThemes = explode(',', $modSettings['enableThemes']);
  723. // Are we disabling it?
  724. if (isset($_GET['disabled']))
  725. $enableThemes = array_diff($enableThemes, array($themeID));
  726. // Nope? then enable it!
  727. else
  728. $enableThemes[] = (string) $themeID;
  729. // Update the setting.
  730. $enableThemes = strtr(implode(',', $enableThemes), array(',,' => ','));
  731. updateSettings(array('enableThemes' => $enableThemes));
  732. // Done!
  733. redirectexit('action=admin;area=theme;sa=list;' . $context['session_var'] . '=' . $context['session_id'] .';done='. (isset($_GET['disabled']) ? 'disabling' : 'enabling'));
  734. }
  735. /**
  736. * Choose a theme from a list.
  737. * allows an user or administrator to pick a new theme with an interface.
  738. * - can edit everyone's (u = 0), guests' (u = -1), or a specific user's.
  739. * - uses the Themes template. (pick sub template.)
  740. * - accessed with ?action=admin;area=theme;sa=pick.
  741. * @todo thought so... Might be better to split this file in ManageThemes and Themes,
  742. * with centralized admin permissions on ManageThemes.
  743. */
  744. function PickTheme()
  745. {
  746. global $txt, $context, $modSettings, $user_info, $language, $smcFunc, $settings, $scripturl;
  747. loadLanguage('Profile');
  748. loadTemplate('Themes');
  749. // Build the link tree.
  750. $context['linktree'][] = array(
  751. 'url' => $scripturl . '?action=theme;sa=pick;u=' . (!empty($_REQUEST['u']) ? (int) $_REQUEST['u'] : 0),
  752. 'name' => $txt['theme_pick'],
  753. );
  754. $context['default_theme_id'] = $modSettings['theme_default'];
  755. $_SESSION['id_theme'] = 0;
  756. if (isset($_GET['id']))
  757. $_GET['th'] = $_GET['id'];
  758. // Saving a variant cause JS doesn't work - pretend it did ;)
  759. if (isset($_POST['save']))
  760. {
  761. // Which theme?
  762. foreach ($_POST['save'] as $k => $v)
  763. $_GET['th'] = (int) $k;
  764. if (isset($_POST['vrt'][$k]))
  765. $_GET['vrt'] = $_POST['vrt'][$k];
  766. }
  767. // Have we made a decision, or are we just browsing?
  768. if (isset($_GET['th']))
  769. {
  770. checkSession('get');
  771. $_GET['th'] = (int) $_GET['th'];
  772. // Save for this user.
  773. if (!isset($_REQUEST['u']) || !allowedTo('admin_forum'))
  774. {
  775. updateMemberData($user_info['id'], array('id_theme' => (int) $_GET['th']));
  776. // A variants to save for the user?
  777. if (!empty($_GET['vrt']))
  778. {
  779. $smcFunc['db_insert']('replace',
  780. '{db_prefix}themes',
  781. array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  782. array($_GET['th'], $user_info['id'], 'theme_variant', $_GET['vrt']),
  783. array('id_theme', 'id_member', 'variable')
  784. );
  785. cache_put_data('theme_settings-' . $_GET['th'] . ':' . $user_info['id'], null, 90);
  786. $_SESSION['id_variant'] = 0;
  787. }
  788. redirectexit('action=profile;area=theme');
  789. }
  790. // If changing members or guests - and there's a variant - assume changing default variant.
  791. if (!empty($_GET['vrt']) && ($_REQUEST['u'] == '0' || $_REQUEST['u'] == '-1'))
  792. {
  793. $smcFunc['db_insert']('replace',
  794. '{db_prefix}themes',
  795. array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  796. array($_GET['th'], 0, 'default_variant', $_GET['vrt']),
  797. array('id_theme', 'id_member', 'variable')
  798. );
  799. // Make it obvious that it's changed
  800. cache_put_data('theme_settings-' . $_GET['th'], null, 90);
  801. }
  802. // For everyone.
  803. if ($_REQUEST['u'] == '0')
  804. {
  805. updateMemberData(null, array('id_theme' => (int) $_GET['th']));
  806. // Remove any custom variants.
  807. if (!empty($_GET['vrt']))
  808. {
  809. $smcFunc['db_query']('', '
  810. DELETE FROM {db_prefix}themes
  811. WHERE id_theme = {int:current_theme}
  812. AND variable = {string:theme_variant}',
  813. array(
  814. 'current_theme' => (int) $_GET['th'],
  815. 'theme_variant' => 'theme_variant',
  816. )
  817. );
  818. }
  819. redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
  820. }
  821. // Change the default/guest theme.
  822. elseif ($_REQUEST['u'] == '-1')
  823. {
  824. updateSettings(array('theme_guests' => (int) $_GET['th']));
  825. redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
  826. }
  827. // Change a specific member's theme.
  828. else
  829. {
  830. // The forum's default theme is always 0 and we
  831. if (isset($_GET['th']) && $_GET['th'] == 0)
  832. $_GET['th'] = $modSettings['theme_guests'];
  833. updateMemberData((int) $_REQUEST['u'], array('id_theme' => (int) $_GET['th']));
  834. if (!empty($_GET['vrt']))
  835. {
  836. $smcFunc['db_insert']('replace',
  837. '{db_prefix}themes',
  838. array('id_theme' => 'int', 'id_member' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  839. array($_GET['th'], (int) $_REQUEST['u'], 'theme_variant', $_GET['vrt']),
  840. array('id_theme', 'id_member', 'variable')
  841. );
  842. cache_put_data('theme_settings-' . $_GET['th'] . ':' . (int) $_REQUEST['u'], null, 90);
  843. if ($user_info['id'] == $_REQUEST['u'])
  844. $_SESSION['id_variant'] = 0;
  845. }
  846. redirectexit('action=profile;u=' . (int) $_REQUEST['u'] . ';area=theme');
  847. }
  848. }
  849. // Figure out who the member of the minute is, and what theme they've chosen.
  850. if (!isset($_REQUEST['u']) || !allowedTo('admin_forum'))
  851. {
  852. $context['current_member'] = $user_info['id'];
  853. $context['current_theme'] = $user_info['theme'];
  854. }
  855. // Everyone can't chose just one.
  856. elseif ($_REQUEST['u'] == '0')
  857. {
  858. $context['current_member'] = 0;
  859. $context['current_theme'] = 0;
  860. }
  861. // Guests and such...
  862. elseif ($_REQUEST['u'] == '-1')
  863. {
  864. $context['current_member'] = -1;
  865. $context['current_theme'] = $modSettings['theme_guests'];
  866. }
  867. // Someones else :P.
  868. else
  869. {
  870. $context['current_member'] = (int) $_REQUEST['u'];
  871. $request = $smcFunc['db_query']('', '
  872. SELECT id_theme
  873. FROM {db_prefix}members
  874. WHERE id_member = {int:current_member}
  875. LIMIT 1',
  876. array(
  877. 'current_member' => $context['current_member'],
  878. )
  879. );
  880. list ($context['current_theme']) = $smcFunc['db_fetch_row']($request);
  881. $smcFunc['db_free_result']($request);
  882. }
  883. // Get the theme name and descriptions.
  884. $context['available_themes'] = array();
  885. if (!empty($modSettings['knownThemes']))
  886. {
  887. $request = $smcFunc['db_query']('', '
  888. SELECT id_theme, variable, value
  889. FROM {db_prefix}themes
  890. WHERE variable IN ({string:name}, {string:theme_url}, {string:theme_dir}, {string:images_url}, {string:disable_user_variant})' . (!allowedTo('admin_forum') ? '
  891. AND id_theme IN ({array_string:known_themes})' : '') . '
  892. AND id_theme != {int:default_theme}
  893. AND id_member = {int:no_member}
  894. AND id_theme IN ({array_string:enable_themes})',
  895. array(
  896. 'default_theme' => 0,
  897. 'name' => 'name',
  898. 'no_member' => 0,
  899. 'theme_url' => 'theme_url',
  900. 'theme_dir' => 'theme_dir',
  901. 'images_url' => 'images_url',
  902. 'disable_user_variant' => 'disable_user_variant',
  903. 'known_themes' => explode(',', $modSettings['knownThemes']),
  904. 'enable_themes' => explode(',', $modSettings['enableThemes']),
  905. )
  906. );
  907. while ($row = $smcFunc['db_fetch_assoc']($request))
  908. {
  909. if (!isset($context['available_themes'][$row['id_theme']]))
  910. $context['available_themes'][$row['id_theme']] = array(
  911. 'id' => $row['id_theme'],
  912. 'selected' => $context['current_theme'] == $row['id_theme'],
  913. 'num_users' => 0
  914. );
  915. $context['available_themes'][$row['id_theme']][$row['variable']] = $row['value'];
  916. }
  917. $smcFunc['db_free_result']($request);
  918. }
  919. // Okay, this is a complicated problem: the default theme is 1, but they aren't allowed to access 1!
  920. if (!isset($context['available_themes'][$modSettings['theme_guests']]))
  921. {
  922. $context['available_themes'][0] = array(
  923. 'num_users' => 0
  924. );
  925. $guest_theme = 0;
  926. }
  927. else
  928. $guest_theme = $modSettings['theme_guests'];
  929. $request = $smcFunc['db_query']('', '
  930. SELECT id_theme, COUNT(*) AS the_count
  931. FROM {db_prefix}members
  932. GROUP BY id_theme
  933. ORDER BY id_theme DESC',
  934. array(
  935. )
  936. );
  937. while ($row = $smcFunc['db_fetch_assoc']($request))
  938. {
  939. // Figure out which theme it is they are REALLY using.
  940. if (!empty($modSettings['knownThemes']) && !in_array($row['id_theme'], explode(',',$modSettings['knownThemes'])))
  941. $row['id_theme'] = $guest_theme;
  942. elseif (empty($modSettings['theme_allow']))
  943. $row['id_theme'] = $guest_theme;
  944. if (isset($context['available_themes'][$row['id_theme']]))
  945. $context['available_themes'][$row['id_theme']]['num_users'] += $row['the_count'];
  946. else
  947. $context['available_themes'][$guest_theme]['num_users'] += $row['the_count'];
  948. }
  949. $smcFunc['db_free_result']($request);
  950. // Get any member variant preferences.
  951. $variant_preferences = array();
  952. if ($context['current_member'] > 0)
  953. {
  954. $request = $smcFunc['db_query']('', '
  955. SELECT id_theme, value
  956. FROM {db_prefix}themes
  957. WHERE variable = {string:theme_variant}
  958. AND id_member IN ({array_int:id_member})
  959. ORDER BY id_member ASC',
  960. array(
  961. 'theme_variant' => 'theme_variant',
  962. 'id_member' => isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'pick' ? array(-1, $context['current_member']) : array(-1),
  963. )
  964. );
  965. while ($row = $smcFunc['db_fetch_assoc']($request))
  966. $variant_preferences[$row['id_theme']] = $row['value'];
  967. $smcFunc['db_free_result']($request);
  968. }
  969. // Save the setting first.
  970. $current_images_url = $settings['images_url'];
  971. $current_theme_variants = !empty($settings['theme_variants']) ? $settings['theme_variants'] : array();
  972. foreach ($context['available_themes'] as $id_theme => $theme_data)
  973. {
  974. // Don't try to load the forum or board default theme's data... it doesn't have any!
  975. if ($id_theme == 0)
  976. continue;
  977. // The thumbnail needs the correct path.
  978. $settings['images_url'] = &$theme_data['images_url'];
  979. if (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php'))
  980. include($theme_data['theme_dir'] . '/languages/Settings.' . $user_info['language'] . '.php');
  981. elseif (file_exists($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php'))
  982. include($theme_data['theme_dir'] . '/languages/Settings.' . $language . '.php');
  983. else
  984. {
  985. $txt['theme_thumbnail_href'] = $theme_data['images_url'] . '/thumbnail.png';
  986. $txt['theme_description'] = '';
  987. }
  988. $context['available_themes'][$id_theme]['thumbnail_href'] = $txt['theme_thumbnail_href'];
  989. $context['available_themes'][$id_theme]['description'] = $txt['theme_description'];
  990. // Are there any variants?
  991. if (file_exists($theme_data['theme_dir'] . '/index.template.php') && (empty($theme_data['disable_user_variant']) || allowedTo('admin_forum')))
  992. {
  993. $file_contents = implode('', file($theme_data['theme_dir'] . '/index.template.php'));
  994. if (preg_match('~\$settings\[\'theme_variants\'\]\s*=(.+?);~', $file_contents, $matches))
  995. {
  996. $settings['theme_variants'] = array();
  997. // Fill settings up.
  998. eval('global $settings;' . $matches[0]);
  999. if (!empty($settings['theme_variants']))
  1000. {
  1001. loadLanguage('Settings');
  1002. $context['available_themes'][$id_theme]['variants'] = array();
  1003. foreach ($settings['theme_variants'] as $variant)
  1004. $context['available_themes'][$id_theme]['variants'][$variant] = array(
  1005. 'label' => isset($txt['variant_' . $variant]) ? $txt['variant_' . $variant] : $variant,
  1006. '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'),
  1007. );
  1008. $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]));
  1009. if (!isset($context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail']))
  1010. $context['available_themes'][$id_theme]['selected_variant'] = $settings['theme_variants'][0];
  1011. $context['available_themes'][$id_theme]['thumbnail_href'] = $context['available_themes'][$id_theme]['variants'][$context['available_themes'][$id_theme]['selected_variant']]['thumbnail'];
  1012. // Allow themes to override the text.
  1013. $context['available_themes'][$id_theme]['pick_label'] = isset($txt['variant_pick']) ? $txt['variant_pick'] : $txt['theme_pick_variant'];
  1014. }
  1015. }
  1016. }
  1017. }
  1018. // Then return it.
  1019. $settings['images_url'] = $current_images_url;
  1020. $settings['theme_variants'] = $current_theme_variants;
  1021. // As long as we're not doing the default theme...
  1022. if (!isset($_REQUEST['u']) || $_REQUEST['u'] >= 0)
  1023. {
  1024. if ($guest_theme != 0)
  1025. $context['available_themes'][0] = $context['available_themes'][$guest_theme];
  1026. $context['available_themes'][0]['id'] = 0;
  1027. $context['available_themes'][0]['name'] = $txt['theme_forum_default'];
  1028. $context['available_themes'][0]['selected'] = $context['current_theme'] == 0;
  1029. $context['available_themes'][0]['description'] = $txt['theme_global_description'];
  1030. }
  1031. ksort($context['available_themes']);
  1032. $context['page_title'] = $txt['theme_pick'];
  1033. $context['sub_template'] = 'pick';
  1034. }
  1035. /**
  1036. * Installs new themes, calls the respective function according to the install type.
  1037. * - puts themes in $boardurl/Themes.
  1038. * - assumes the gzip has a root directory in it. (ie default.)
  1039. * Requires admin_forum.
  1040. * Accessed with ?action=admin;area=theme;sa=install.
  1041. */
  1042. function ThemeInstall()
  1043. {
  1044. global $sourcedir, $txt, $context, $boarddir, $boardurl;
  1045. global $themedir, $themeurl, $smcFunc;
  1046. checkSession('request');
  1047. isAllowedTo('admin_forum');
  1048. require_once($sourcedir . '/Subs-Package.php');
  1049. // Make it easier to change the path and url.
  1050. $themedir = $boarddir . '/Themes';
  1051. $themeurl = $boardurl . '/Themes';
  1052. loadTemplate('Themes');
  1053. $subActions = array(
  1054. 'file' => 'InstallFile',
  1055. 'copy' => 'InstallCopy',
  1056. 'dir' => 'InstallDir',
  1057. );
  1058. // Is there a function to call?
  1059. if (isset($_GET['do']) && !empty($_GET['do']) && isset($subActions[$_GET['do']]))
  1060. {
  1061. $action = $smcFunc['htmlspecialchars'](trim($_GET['do']));
  1062. // Got any info from the specific form?
  1063. if (!isset($_POST['save_'. $action]))
  1064. fatal_lang_error('theme_install_no_action', false);
  1065. validateToken('admin-t-'. $action);
  1066. // Hopefully the themes directory is writable, or we might have a problem.
  1067. if (!is_writable($themedir))
  1068. fatal_lang_error('theme_install_write_error', 'critical');
  1069. // Call the function and handle the result.
  1070. $result = $subActions[$action]();
  1071. // Everything went better than expected!
  1072. if (!empty($result))
  1073. {
  1074. $context['sub_template'] = 'installed';
  1075. $context['page_title'] = $txt['theme_installed'];
  1076. $context['installed_theme'] = $result;
  1077. }
  1078. }
  1079. // Nope, show a nice error.
  1080. else
  1081. fatal_lang_error('theme_install_no_action', false);
  1082. }
  1083. /**
  1084. * Installs a theme from a theme package.
  1085. *
  1086. * 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.
  1087. * @return array The newly created theme's info.
  1088. */
  1089. function InstallFile()
  1090. {
  1091. global $themedir, $themeurl, $context;
  1092. // Set a temp dir for dumping all required files on it.
  1093. $dirtemp = $themedir .'/temp';
  1094. // Create the temp dir.
  1095. mkdir($dirtemp, 0777);
  1096. // Hopefully the temp directory is writable, or we might have a problem.
  1097. if (!is_writable($dirtemp))
  1098. {
  1099. // Lets give it a try.
  1100. @chmod($dirtmp, '0755');
  1101. // How about now?
  1102. if (!is_writable($dirtemp))
  1103. fatal_lang_error('theme_install_write_error', 'critical');
  1104. }
  1105. // This happens when the admin session is gone and the user has to login again.
  1106. if (!isset($_FILES) || !isset($_FILES['theme_gz']) || empty($_FILES['theme_gz']))
  1107. redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
  1108. // Another error check layer, something went wrong with the upload.
  1109. if (isset($_FILES['theme_gz']['error']) && $_FILES['theme_gz']['error'] != 0)
  1110. fatal_lang_error('theme_install_error_file_'. $_FILES['theme_gz']['error'], false);
  1111. // Get the theme's name.
  1112. $name = strtok(basename($_FILES['theme_gz']['name']));
  1113. $name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $name);
  1114. // Start setting some vars.
  1115. $context['to_install'] = array(
  1116. 'theme_dir' => $themedir . '/' . $name,
  1117. 'theme_url' => $themeurl . '/' . $name,
  1118. 'images_url' => $themeurl . '/' . $name . '/images',
  1119. 'name' => $name,
  1120. );
  1121. // Extract the file on the proper themes dir.
  1122. $extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $dirtemp, false, true);
  1123. if ($extracted)
  1124. {
  1125. // Read its info form the XML file.
  1126. $theme_info = get_theme_info($dirtemp);
  1127. $context['to_install'] += $theme_info;
  1128. // Install the theme. theme_install() will return the new installed ID.
  1129. $context['to_install']['id'] = theme_install($context['to_install']);
  1130. // Rename the temp dir to the actual theme name.
  1131. rename($dirtemp, $context['to_install']['theme_dir']);
  1132. // return all the info.
  1133. return $context['to_install'];
  1134. }
  1135. else
  1136. fatal_lang_error('theme_install_error_title', false);
  1137. }
  1138. /**
  1139. * Makes a copy from the default theme, assigns a name for it and installs it.
  1140. *
  1141. * Creates a new .xml file containing all the theme's info.
  1142. * @return array The newly created theme's info.
  1143. */
  1144. function InstallCopy()
  1145. {
  1146. global $themedir, $themeurl, $settings, $smcFunc, $context;
  1147. global $forum_version;
  1148. // There's gotta be something to work with.
  1149. if (!isset($_REQUEST['copy']) || empty($_REQUEST['copy']))
  1150. fatal_lang_error('theme_install_error_title', false);
  1151. // Get a cleaner version.
  1152. $name = preg_replace('~[^A-Za-z0-9_\- ]~', '', $_REQUEST['copy']);
  1153. // Is there a theme already named like this?
  1154. if (file_exists($themedir .'/'. $name))
  1155. fatal_lang_error('theme_install_already_dir', false);
  1156. // This is a brand new theme so set all possible values.
  1157. $context['to_install'] = array(
  1158. 'theme_dir' => $themedir . '/' . $name,
  1159. 'theme_url' => $themeurl . '/' . $name,
  1160. 'name' => $name,
  1161. 'images_url' => $themeurl . '/' . $name . '/images',
  1162. 'version' => '1.0',
  1163. 'install_for' => '2.1 - 2.1.99, '. strtr($forum_version, array('SMF ' => '')),
  1164. 'based_on' => '',
  1165. 'based_on_dir' => $themedir . '/default',
  1166. );
  1167. // Create the specific dir.
  1168. umask(0);
  1169. mkdir($context['to_install']['theme_dir'], 0777);
  1170. // Buy some time.
  1171. @set_time_limit(600);
  1172. if (function_exists('apache_reset_timeout'))
  1173. @apache_reset_timeout();
  1174. // Create subdirectories for css and javascript files.
  1175. mkdir($context['to_install']['theme_dir'] . '/css', 0777);
  1176. mkdir($context['to_install']['theme_dir'] . '/scripts', 0777);
  1177. // Copy over the default non-theme files.
  1178. $to_copy = array('/index.php', '/index.template.php', '/css/index.css', '/css/index.responsive.css', '/css/rtl.css', '/css/calendar.css', '/css/calendar.rtl.css', '/css/admin.css', '/scripts/theme.js');
  1179. foreach ($to_copy as $file)
  1180. {
  1181. copy($settings['default_theme_dir'] . $file, $context['to_install']['theme_dir'] . $file);
  1182. @chmod($context['to_install']['theme_dir'] . $file, 0777);
  1183. }
  1184. // And now the entire images directory!
  1185. copytree($settings['default_theme_dir'] . '/images', $context['to_install']['theme_dir'] . '/images');
  1186. package_flush_cache();
  1187. // Lets get some data for the new theme.
  1188. $request = $smcFunc['db_query']('', '
  1189. SELECT variable, value
  1190. FROM {db_prefix}themes
  1191. WHERE variable IN ({string:theme_templates}, {string:theme_layers})
  1192. AND id_member = {int:no_member}
  1193. AND id_theme = {int:default_theme}',
  1194. array(
  1195. 'no_member' => 0,
  1196. 'default_theme' => 1,
  1197. 'theme_templates' => 'theme_templates',
  1198. 'theme_layers' => 'theme_layers',
  1199. )
  1200. );
  1201. while ($row = $smcFunc['db_fetch_assoc']($request))
  1202. {
  1203. if ($row['variable'] == 'theme_templates')
  1204. $theme_templates = $row['value'];
  1205. elseif ($row['variable'] == 'theme_layers')
  1206. $theme_layers = $row['value'];
  1207. else
  1208. continue;
  1209. }
  1210. $smcFunc['db_free_result']($request);
  1211. $context['to_install'] += array(
  1212. 'theme_layers' => empty($theme_layers) ? 'html,body' : $theme_layers,
  1213. 'theme_templates' => empty($theme_templates) ? 'index' : $theme_templates,
  1214. );
  1215. // Lets add a theme_info.xml to this theme.
  1216. $xml_info = '<' . '?xml version="1.0"?' . '>
  1217. <theme-info xmlns="http://www.simplemachines.org/xml/theme-info" xmlns:smf="http://www.simplemachines.org/">
  1218. <!-- For the id, always use something unique - put your name, a colon, and then the package name. -->
  1219. <id>smf:' . $smcFunc['strtolower']($context['to_install']['name']) . '</id>
  1220. <!-- The theme\'s version, please try to use semantic versioning. -->
  1221. <version>1.0</version>
  1222. <!-- Install for, the SMF versions this theme was designed for. Uses the same wildcards used in the packager manager. This field is mandatory. -->
  1223. <install for="'. $context['to_install']['install_for'] .'" />
  1224. <!-- Theme name, used purely for aesthetics. -->
  1225. <name>' . $context['to_install']['name'] . '</name>
  1226. <!-- Author: your email address or contact information. The name attribute is optional. -->
  1227. <author name="Simple Machines">info@simplemachines.org</author>
  1228. <!-- Website... where to get updates and more information. -->
  1229. <website>http://www.simplemachines.org/</website>
  1230. <!-- Template layers to use, defaults to "html,body". -->
  1231. <layers>' . $context['to_install']['theme_layers'] . '</layers>
  1232. <!-- Templates to load on startup. Default is "index". -->
  1233. <templates>' . $context['to_install']['theme_templates'] . '</templates>
  1234. <!-- Base this theme off another? Default is blank, or no. It could be "default". -->
  1235. <based-on></based-on>
  1236. </theme-info>';
  1237. // Now write it.
  1238. $fp = @fopen($context['to_install']['theme_dir'] . '/theme_info.xml', 'w+');
  1239. if ($fp)
  1240. {
  1241. fwrite($fp, $xml_info);
  1242. fclose($fp);
  1243. }
  1244. // Install the theme. theme_install() will take care of possible errors.
  1245. $context['to_install']['id'] = theme_install($context['to_install']);
  1246. // return the info.
  1247. return $context['to_install'];
  1248. }
  1249. /**
  1250. * Install a theme from a specific dir
  1251. *
  1252. * Assumes the dir is located on the main Themes dir. Ends execution with fatal_lang_error() on any error.
  1253. * @return array The newly created theme's info.
  1254. */
  1255. function InstallDir()
  1256. {
  1257. global $themedir, $themeurl, $context;
  1258. // Cannot use the theme dir as a theme dir.
  1259. if (!isset($_REQUEST['theme_dir']) || empty($_REQUEST['theme_dir']) || rtrim(realpath($_REQUEST['theme_dir']), '/\\') == realpath($themedir))
  1260. fatal_lang_error('theme_install_invalid_dir', false);
  1261. // Check is there is "something" on the dir.
  1262. elseif (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml'))
  1263. fatal_lang_error('theme_install_error', false);
  1264. $name = basename($_REQUEST['theme_dir']);
  1265. $name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $name);
  1266. // All good! set some needed vars.
  1267. $context['to_install'] = array(
  1268. 'theme_dir' => $_REQUEST['theme_dir'],
  1269. 'theme_url' => $themeurl . '/' . $name,
  1270. 'name' => $name,
  1271. 'images_url' => $themeurl . '/' . $name . '/images',
  1272. );
  1273. // Read its info form the XML file.
  1274. $theme_info = get_theme_info($context['to_install']['theme_dir']);
  1275. $context['to_install'] += $theme_info;
  1276. // Install the theme. theme_install() will take care of possible errors.
  1277. $context['to_install']['id'] = theme_install($context['to_install']);
  1278. // return the info.
  1279. return $context['to_install'];
  1280. }
  1281. /**
  1282. * Possibly the simplest and best example of how to use the template system.
  1283. * - allows the theme to take care of actions.
  1284. * - happens if $settings['catch_action'] is set and action isn't found
  1285. * in the action array.
  1286. * - can use a template, layers, sub_template, filename, and/or function.
  1287. */
  1288. function WrapAction()
  1289. {
  1290. global $context, $settings;
  1291. // Load any necessary template(s)?
  1292. if (isset($settings['catch_action']['template']))
  1293. {
  1294. // Load both the template and language file. (but don't fret if t…

Large files files are truncated, but you can click here to view the full file