Subs-Themes.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <?php
  2. /**
  3. * Helper file for handing themes.
  4. *
  5. * Simple Machines Forum (SMF)
  6. *
  7. * @package SMF
  8. * @author Simple Machines
  9. *
  10. * @copyright 2013 Simple Machines and individual contributors
  11. * @license http://www.simplemachines.org/about/smf/license.php BSD
  12. *
  13. * @version 2.1 Alpha 1
  14. */
  15. if (!defined('SMF'))
  16. die('No direct access...');
  17. function get_single_theme($id)
  18. {
  19. global $smcFunc, $modSettings;
  20. // No data, no fun!
  21. if (empty($id))
  22. return false;
  23. // List of all possible values.
  24. $themeValues = array(
  25. 'theme_dir',
  26. 'images_url',
  27. 'theme_url',
  28. 'name',
  29. 'theme_layers',
  30. 'theme_templates',
  31. 'version',
  32. 'install_for',
  33. 'based_on',
  34. );
  35. $single = array(
  36. 'id' => (int) $id,
  37. );
  38. // Make our known/enable themes a little easier to work with.
  39. $knownThemes = !empty($modSettings['knownThemes']) ? explode(',',$modSettings['knownThemes']) : array();
  40. $enableThemes = !empty($modSettings['enableThemes']) ? explode(',',$modSettings['enableThemes']) : array();
  41. $request = $smcFunc['db_query']('', '
  42. SELECT id_theme, variable, value
  43. FROM {db_prefix}themes
  44. WHERE variable IN ({array_string:theme_values})
  45. AND id_theme = ({int:id_theme})
  46. AND id_member = {int:no_member}',
  47. array(
  48. 'theme_values' => $themeValues,
  49. 'id_theme' => $id,
  50. 'no_member' => 0,
  51. )
  52. );
  53. while ($row = $smcFunc['db_fetch_assoc']($request))
  54. $single[$row['variable']] = $row['value'];
  55. // Fix the path and tell if its a valid one.
  56. $single['theme_dir'] = realpath($single['theme_dir']);
  57. $single['valid_path'] = file_exists($single['theme_dir']) && is_dir($single['theme_dir']);
  58. $single['known'] = in_array($single['id'], $knownThemes);
  59. $single['enable'] = in_array($single['id'], $enableThemes);
  60. return $single;
  61. }
  62. function get_all_themes($enable_only = false)
  63. {
  64. global $modSettings, $context, $smcFunc;
  65. // Make our known/enable themes a little easier to work with.
  66. $knownThemes = !empty($modSettings['knownThemes']) ? explode(',',$modSettings['knownThemes']) : array();
  67. $enableThemes = !empty($modSettings['enableThemes']) ? explode(',',$modSettings['enableThemes']) : array();
  68. // List of all possible themes values.
  69. $themeValues = array(
  70. 'theme_dir',
  71. 'images_url',
  72. 'theme_url',
  73. 'name',
  74. 'theme_layers',
  75. 'theme_templates',
  76. 'version',
  77. 'install_for',
  78. 'based_on',
  79. );
  80. // So, what is it going to be?
  81. $query_where = $enable_only ? $enableThemes : $knownThemes;
  82. // Perform the query as requested.
  83. $request = $smcFunc['db_query']('', '
  84. SELECT id_theme, variable, value
  85. FROM {db_prefix}themes
  86. WHERE variable IN ({array_string:theme_values})
  87. AND id_theme IN ({array_string:query_where})
  88. AND id_member = {int:no_member}',
  89. array(
  90. 'query_where' => $query_where,
  91. 'theme_values' => $themeValues,
  92. 'no_member' => 0,
  93. )
  94. );
  95. $context['themes'] = array();
  96. while ($row = $smcFunc['db_fetch_assoc']($request))
  97. {
  98. $context['themes'][$row['id_theme']]['id'] = (int) $row['id_theme'];
  99. // Fix the path and tell if its a valid one.
  100. if ($row['variable'] == 'theme_dir')
  101. {
  102. $context['themes'][$row['id_theme']][$row['variable']] = realpath($row['value']);
  103. $context['themes'][$row['id_theme']]['valid_path'] = file_exists(realpath($row['value'])) && is_dir(realpath($row['value']));
  104. }
  105. $context['themes'][$row['id_theme']]['known'] = in_array($row['id_theme'], $knownThemes);
  106. $context['themes'][$row['id_theme']]['enable'] = in_array($row['id_theme'], $enableThemes);
  107. $context['themes'][$row['id_theme']][$row['variable']] = $row['value'];
  108. }
  109. $smcFunc['db_free_result']($request);
  110. return $context['themes'];
  111. }
  112. function get_theme_info($path)
  113. {
  114. global $sourcedir, $forum_version, $txt, $scripturl, $context;
  115. global $explicit_images;
  116. if (empty($path))
  117. return false;
  118. $xml_data = array();
  119. $explicit_images = false;
  120. // Perhaps they are trying to install a mod, lets tell them nicely this is the wrong function.
  121. if (file_exists($path . '/package-info.xml'))
  122. {
  123. loadLanguage('Errors');
  124. // We need to delete the dir otherwise the next time you try to install a theme you will get the same error.
  125. remove_dir($path);
  126. $txt['package_get_error_is_mod'] = str_replace('{MANAGEMODURL}', $scripturl . '?action=admin;area=packages;' . $context['session_var'] . '=' . $context['session_id'], $txt['package_get_error_is_mod']);
  127. fatal_lang_error('package_theme_upload_error_broken', false, $txt['package_get_error_is_mod']);
  128. }
  129. // Parse theme-info.xml into an xmlArray.
  130. require_once($sourcedir . '/Class-Package.php');
  131. $theme_info_xml = new xmlArray(file_get_contents($path . '/theme_info.xml'));
  132. // Error message, there isn't any valid info.
  133. if (!$theme_info_xml->exists('theme-info[0]'))
  134. {
  135. remove_dir($path);
  136. fatal_lang_error('package_get_error_packageinfo_corrupt', false);
  137. }
  138. // Check for compatibility with 2.1 or greater.
  139. if (!$theme_info_xml->exists('theme-info/install'))
  140. {
  141. remove_dir($path);
  142. fatal_lang_error('package_get_error_theme_not_compatible', false, $forum_version);
  143. }
  144. // So, we have an install tag which is cool and stuff but we also need to check it and match your current SMF version...
  145. $the_version = strtr($forum_version, array('SMF ' => ''));
  146. $install_versions = $theme_info_xml->path('theme-info/install/@for');
  147. // The theme isn't compatible with the current SMF version.
  148. if (!$install_versions || !matchPackageVersion($the_version, $install_versions))
  149. {
  150. remove_dir($path);
  151. fatal_lang_error('package_get_error_theme_not_compatible', false, $forum_version);
  152. }
  153. $theme_info_xml = $theme_info_xml->path('theme-info[0]');
  154. $theme_info_xml = $theme_info_xml->to_array();
  155. $xml_elements = array(
  156. 'theme_layers' => 'layers',
  157. 'theme_templates' => 'templates',
  158. 'based_on' => 'based-on',
  159. 'version' => 'version',
  160. );
  161. // Assign the values to be stored.
  162. foreach ($xml_elements as $var => $name)
  163. if (!empty($theme_info_xml[$name]))
  164. $xml_data[$var] = $theme_info_xml[$name];
  165. // Add the supported versions.
  166. $xml_data['install_for'] = $install_versions;
  167. // Overwrite the default images folder.
  168. if (!empty($theme_info_xml['images']))
  169. {
  170. $xml_data['images_url'] = $path . '/' . $theme_info_xml['images'];
  171. $explicit_images = true;
  172. }
  173. if (!empty($theme_info_xml['extra']))
  174. $xml_data += unserialize($theme_info_xml['extra']);
  175. return $xml_data;
  176. }
  177. function theme_install($to_install = array())
  178. {
  179. global $smcFunc, $context, $themedir, $themeurl, $modSettings;
  180. global $settings, $explicit_images;
  181. // External use? no problem!
  182. if ($to_install)
  183. $context['to_install'] = $to_install;
  184. // One last check.
  185. if (empty($context['to_install']['theme_dir']) || basename($context['to_install']['theme_dir']) == 'Themes')
  186. fatal_lang_error('theme_install_invalid_dir', false);
  187. // OK, is this a newer version of an already installed theme?
  188. if (!empty($context['to_install']['version']))
  189. {
  190. $to_update = array();
  191. $request = $smcFunc['db_query']('', '
  192. SELECT th.value AS name, th.id_theme, th2.value AS version
  193. FROM {db_prefix}themes AS th
  194. INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme
  195. AND th2.id_member = {int:no_member}
  196. AND th2.variable = {string:version})
  197. WHERE th.id_member = {int:no_member}
  198. AND th.variable = {string:name}
  199. AND th.value LIKE {string:name_value}
  200. LIMIT 1',
  201. array(
  202. 'no_member' => 0,
  203. 'name' => 'name',
  204. 'version' => 'version',
  205. 'name_value' => '%'. $context['to_install']['name'] .'%',
  206. )
  207. );
  208. $to_update = $smcFunc['db_fetch_assoc']($request);
  209. $smcFunc['db_free_result']($request);
  210. // Got something, lets figure it out what to do next.
  211. if (!empty($to_update) && !empty($to_update['version']))
  212. switch (compareVersions($context['to_install']['version'], $to_update['version']))
  213. {
  214. case 0: // This is exactly the same theme.
  215. case -1: // The one being installed is older than the one already installed.
  216. default: // Any other possible result.
  217. fatal_lang_error('package_get_error_theme_no_new_version', false, array($context['to_install']['version'], $to_update['version']));
  218. break;
  219. case 1: // Got a newer version, update the old entry.
  220. $smcFunc['db_query']('', '
  221. UPDATE {db_prefix}themes
  222. SET value = {string:new_value}
  223. WHERE variable = {string:version}
  224. AND id_theme = {int:id_theme}',
  225. array(
  226. 'new_value' => $context['to_install']['version'],
  227. 'version' => 'version',
  228. 'id_theme' => $to_update['id_theme'],
  229. )
  230. );
  231. // Done with the update, tell the user about it.
  232. $context['to_install']['updated'] = true;
  233. $context['to_install']['id'] = $to_update['id_theme'];
  234. return $context['to_install'];
  235. break; // Just for reference.
  236. }
  237. }
  238. if (!empty($context['to_install']['based_on']))
  239. {
  240. // No need for elaborated stuff when the theme is based on the default one.
  241. if ($context['to_install']['based_on'] == 'default')
  242. {
  243. $context['to_install']['theme_url'] = $settings['default_theme_url'];
  244. $context['to_install']['images_url'] = $settings['default_images_url'];
  245. }
  246. // Custom theme based on another custom theme, lets get some info.
  247. elseif ($context['to_install']['based_on'] != '')
  248. {
  249. $context['to_install']['based_on'] = preg_replace('~[^A-Za-z0-9\-_ ]~', '', $context['to_install']['based_on']);
  250. $request = $smcFunc['db_query']('', '
  251. SELECT th.value AS base_theme_dir, th2.value AS base_theme_url' . (!empty($explicit_images) ? '' : ', th3.value AS images_url') . '
  252. FROM {db_prefix}themes AS th
  253. INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme
  254. AND th2.id_member = {int:no_member}
  255. AND th2.variable = {string:theme_url})' . (!empty($explicit_images) ? '' : '
  256. INNER JOIN {db_prefix}themes AS th3 ON (th3.id_theme = th.id_theme
  257. AND th3.id_member = {int:no_member}
  258. AND th3.variable = {string:images_url})') . '
  259. WHERE th.id_member = {int:no_member}
  260. AND (th.value LIKE {string:based_on} OR th.value LIKE {string:based_on_path})
  261. AND th.variable = {string:theme_dir}
  262. LIMIT 1',
  263. array(
  264. 'no_member' => 0,
  265. 'theme_url' => 'theme_url',
  266. 'images_url' => 'images_url',
  267. 'theme_dir' => 'theme_dir',
  268. 'based_on' => '%/' . $context['to_install']['based_on'],
  269. 'based_on_path' => '%' . "\\" . $context['to_install']['based_on'],
  270. )
  271. );
  272. $temp = $smcFunc['db_fetch_assoc']($request);
  273. $smcFunc['db_free_result']($request);
  274. // Found the based on theme info, add it to the current one being installed.
  275. if (is_array($temp))
  276. {
  277. $context['to_install'] = $temp + $context['to_install'];
  278. if (empty($explicit_images) && !empty($context['to_install']['base_theme_url']))
  279. $context['to_install']['theme_url'] = $context['to_install']['base_theme_url'];
  280. }
  281. // Nope, sorry, couldn't find any theme already installed.
  282. else
  283. fatal_lang_error('package_get_error_theme_no_based_on_found', false, $context['to_install']['based_on']);
  284. }
  285. unset($context['to_install']['based_on']);
  286. }
  287. // Find the newest id_theme.
  288. $result = $smcFunc['db_query']('', '
  289. SELECT MAX(id_theme)
  290. FROM {db_prefix}themes',
  291. array(
  292. )
  293. );
  294. list ($id_theme) = $smcFunc['db_fetch_row']($result);
  295. $smcFunc['db_free_result']($result);
  296. // This will be theme number...
  297. $id_theme++;
  298. $inserts = array();
  299. foreach ($context['to_install'] as $var => $val)
  300. $inserts[] = array($id_theme, $var, $val);
  301. if (!empty($inserts))
  302. $smcFunc['db_insert']('insert',
  303. '{db_prefix}themes',
  304. array('id_theme' => 'int', 'variable' => 'string-255', 'value' => 'string-65534'),
  305. $inserts,
  306. array('id_theme', 'variable')
  307. );
  308. // Update the known and enable Theme's settings.
  309. updateSettings(array('knownThemes' => strtr($modSettings['knownThemes'] . ',' . $id_theme, array(',,' => ','))));
  310. updateSettings(array('enableThemes' => strtr($modSettings['enableThemes'] . ',' . $id_theme, array(',,' => ','))));
  311. return $id_theme;
  312. }
  313. function remove_dir($path)
  314. {
  315. if (empty($path))
  316. return false;
  317. if (is_dir($path))
  318. {
  319. $objects = scandir($path);
  320. foreach ($objects as $object)
  321. if ($object != '.' && $object != '..')
  322. {
  323. if (filetype($path .'/'. $object) == 'dir')
  324. remove_dir($path .'/'.$object);
  325. else
  326. unlink($path .'/'. $object);
  327. }
  328. }
  329. reset($objects);
  330. rmdir($path);
  331. }
  332. function remove_theme($themeID)
  333. {
  334. global $smcFunc, $modSetting;
  335. if (empty($themeID))
  336. return false;
  337. $known = explode(',', $modSettings['knownThemes']);
  338. $enable = explode(',', $modSettings['enableThemes']);
  339. // Remove it from the themes table.
  340. $smcFunc['db_query']('', '
  341. DELETE FROM {db_prefix}themes
  342. WHERE id_theme = {int:current_theme}',
  343. array(
  344. 'current_theme' => $themeID,
  345. )
  346. );
  347. // Update users preferences.
  348. $smcFunc['db_query']('', '
  349. UPDATE {db_prefix}members
  350. SET id_theme = {int:default_theme}
  351. WHERE id_theme = {int:current_theme}',
  352. array(
  353. 'default_theme' => 0,
  354. 'current_theme' => $themeID,
  355. )
  356. );
  357. // Some boards may have it as preferred theme.
  358. $smcFunc['db_query']('', '
  359. UPDATE {db_prefix}boards
  360. SET id_theme = {int:default_theme}
  361. WHERE id_theme = {int:current_theme}',
  362. array(
  363. 'default_theme' => 0,
  364. 'current_theme' => $themeID,
  365. )
  366. );
  367. // Remove it from the list of known themes.
  368. $known = array_diff($known, array($themeID));
  369. // And the enable list too.
  370. $enable = array_diff($enable, array($themeID));
  371. // Back to good old comma separated string.
  372. $known = strtr(implode(',', $known), array(',,' => ','));
  373. $enable = strtr(implode(',', $enable), array(',,' => ','));
  374. // Update the enableThemes list.
  375. updateSettings(array('enableThemes' => $enable));
  376. // Fix it if the theme was the overall default theme.
  377. if ($modSettings['theme_guests'] == $themeID)
  378. updateSettings(array('theme_guests' => '1', 'knownThemes' => $known));
  379. else
  380. updateSettings(array('knownThemes' => $known));
  381. return true;
  382. }
  383. ?>