PageRenderTime 704ms CodeModel.GetById 162ms app.highlight 323ms RepoModel.GetById 119ms app.codeStats 2ms

/forum/Sources/Themes.php

https://github.com/leftnode/nooges.com
PHP | 2508 lines | 1935 code | 308 blank | 265 comment | 301 complexity | 564f1dc790a20e495d6fd5f666a1c915 MD5 | raw file

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

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

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