Themes.php 70 KB

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