PageRenderTime 79ms CodeModel.GetById 4ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 1ms

/php/Sources/Themes.php

https://github.com/dekoza/openshift-smf-2.0.7
PHP | 2163 lines | 1652 code | 284 blank | 227 comment | 296 complexity | a8b00936804ee3266f7516cc54c47bb2 MD5 | raw file

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

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

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