Browse Source

Merge pull request #53 from emanuele45/tokens

Tokens
emanuele45 12 years ago
parent
commit
45ad825a0e

+ 0 - 1
Sources/Admin.php

@@ -866,7 +866,6 @@ function AdminSearchMember()
 
 /**
  * This file allows the user to search the SM online manual for a little of help.
- * @todo wiki search
  */
 function AdminSearchOM()
 {

+ 5 - 19
Sources/DbExtra-mysql.php

@@ -254,34 +254,25 @@ function smf_db_list_tables($db = false, $filter = false)
  */
 function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix, $detected_id;
-	static $start = 0, $num_rows, $fields, $limit, $last_id;
+	global $smcFunc, $db_prefix;
+	static $start = 0, $num_rows, $fields, $limit;
 
 	if ($new_table)
 	{
 		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
 		$start = 0;
-		$last_id = 0;
 	}
 
 	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
-	if ($tableName != $db_prefix . 'messages' || $tableName != $db_prefix . 'topics')
-		$detected_id = 0;
-
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// This is done this way because retrieve data only with LIMIT will become slower after each query
-	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
-	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT /*!40001 SQL_NO_CACHE */ *
-		FROM `' . $tableName . '`' .
-		(!empty($last_id) && !empty($detected_id) ? '
-		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
-		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
+		FROM `' . $tableName . '`
+		LIMIT ' . $start . ', ' . $limit,
 		array(
 			'security_override' => true,
 		)
@@ -318,8 +309,6 @@ function smf_db_insert_sql($tableName, $new_table = false)
 			else
 				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
-		if (!empty($detected_id) && isset($row[$detected_id]))
-			$last_id = $row[$detected_id];
 
 		$data .= '(' . implode(', ', $field_list) . ')' . ',' . $crlf . "\t";
 	}
@@ -340,10 +329,9 @@ function smf_db_insert_sql($tableName, $new_table = false)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix, $detected_id;
+	global $smcFunc, $db_prefix;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
-	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";
@@ -386,8 +374,6 @@ function smf_db_table_sql($tableName)
 
 		// And now any extra information. (such as auto_increment.)
 		$schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf;
-		if ($row['Extra'] == 'auto_increment')
-			$detected_id = $row['Field'];
 	}
 	$smcFunc['db_free_result']($result);
 

+ 5 - 18
Sources/DbExtra-postgresql.php

@@ -149,34 +149,25 @@ function smf_db_list_tables($db = false, $filter = false)
  */
 function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix, $detected_id;
-	static $start = 0, $num_rows, $fields, $limit, $last_id;
+	global $smcFunc, $db_prefix;
+	static $start = 0, $num_rows, $fields, $limit;
 
 	if ($new_table)
 	{
 		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
 		$start = 0;
-		$last_id = 0;
 	}
 
 	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
-	if ($tableName != $db_prefix . 'messages' || $tableName != $db_prefix . 'topics')
-		$detected_id = 0;
-
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// This is done this way because retrieve data only with LIMIT will become slower after each query
-	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
-	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT *
-		FROM ' . $tableName .
-		(!empty($last_id) && !empty($detected_id) ? '
-		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
-		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
+		FROM ' . $tableName . '
+		LIMIT ' . $start . ', ' . $limit,
 		array(
 			'security_override' => true,
 		)
@@ -214,8 +205,6 @@ function smf_db_insert_sql($tableName, $new_table = false)
 			else
 				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
-		if (!empty($detected_id) && isset($row[$detected_id]))
-			$last_id = $row[$detected_id];
 
 		// 'Insert' the data.
 		$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
@@ -237,10 +226,9 @@ function smf_db_insert_sql($tableName, $new_table = false)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix, $detected_id;
+	global $smcFunc, $db_prefix;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
-	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";
@@ -293,7 +281,6 @@ function smf_db_table_sql($tableName)
 				$smcFunc['db_free_result']($count_req);
 				// Get the right bloody start!
 				$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
-				$detected_id = $row['column_name'];
 			}
 		}
 

+ 5 - 17
Sources/DbExtra-sqlite.php

@@ -196,34 +196,25 @@ function smf_db_list_tables($db = false, $filter = false)
  */
 function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix, $detected_id;
-	static $start = 0, $num_rows, $fields, $limit, $last_id;
+	global $smcFunc, $db_prefix;
+	static $start = 0, $num_rows, $fields, $limit;
 
 	if ($new_table)
 	{
 		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
 		$start = 0;
-		$last_id = 0;
 	}
 
 	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
-	if ($tableName != $db_prefix . 'messages' || $tableName != $db_prefix . 'topics')
-		$detected_id = 0;
-
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// This is done this way because retrieve data only with LIMIT will become slower after each query
-	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
-	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT *
-		FROM ' . $tableName .
-		(!empty($last_id) && !empty($detected_id) ? '
-		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
-		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
+		FROM ' . $tableName . '
+		LIMIT ' . $start . ', ' . $limit,
 		array(
 			'security_override' => true,
 		)
@@ -267,8 +258,6 @@ function smf_db_insert_sql($tableName, $new_table = false)
 			else
 				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
-		if (!empty($detected_id) && isset($row[$detected_id]))
-			$last_id = $row[$detected_id];
 
 		// 'Insert' the data.
 		$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
@@ -290,10 +279,9 @@ function smf_db_insert_sql($tableName, $new_table = false)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix, $detected_id;
+	global $smcFunc, $db_prefix;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
-	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";

+ 2 - 2
Sources/LogInOut.php

@@ -131,7 +131,7 @@ function Login2()
 
 	// Are you guessing with a script?
 	checkSession('post');
-	validateToken('login');
+	$tk = validateToken('login');
 	spamProtection('login');
 
 	// Set the login_url if it's not already set (but careful not to send us to an attachment).
@@ -257,7 +257,7 @@ function Login2()
 			return;
 		}
 		// Challenge passed.
-		elseif ($_POST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc))
+		elseif ($_POST['hash_passwrd'] == sha1($user_settings['passwd'] . $sc . $tk))
 			$sha_passwd = $user_settings['passwd'];
 		else
 		{

+ 2 - 4
Sources/ManageAttachments.php

@@ -227,9 +227,7 @@ function ManageAvatarSettings($return_config = false)
 	if ($return_config)
 		return $config_vars;
 
-	// We need these files for the inline permission settings, and the settings template.
-	// @todo is this really needed?
-	require_once($sourcedir . '/ManagePermissions.php');
+	// We need this file for the settings template.
 	require_once($sourcedir . '/ManageServer.php');
 
 	// Saving avatar settings?
@@ -2041,4 +2039,4 @@ function attachDirStatus($dir, $expected_files)
 		return array('ok', false, $dir_size);
 }
 
-?>
+?>

+ 2 - 4
Sources/ManageBoards.php

@@ -808,9 +808,7 @@ function EditBoardSettings($return_config = false)
 	if ($return_config)
 		return $config_vars;
 
-	// Needed for the settings template and inline permission functions.
-	// @todo is this file really needed?
-	require_once($sourcedir . '/ManagePermissions.php');
+	// Needed for the settings template.
 	require_once($sourcedir . '/ManageServer.php');
 
 	// Don't let guests have these permissions.
@@ -849,4 +847,4 @@ function EditBoardSettings($return_config = false)
 	prepareDBSettingContext($config_vars);
 }
 
-?>
+?>

+ 1 - 1
Sources/ManageMaintenance.php

@@ -169,7 +169,7 @@ function MaintainDatabase()
 	//  * safe_mode enable OR
 	//  * cannot change the execution time OR
 	//  * cannot reset timeout
-	if ($context['safe_mode_enable'] || empty($new_time_limit) || $current_time_limit == $new_time_limit || !function_exists('apache_reset_timeout'))
+	if ($context['safe_mode_enable'] || empty($new_time_limit) || ($current_time_limit == $new_time_limit && !function_exists('apache_reset_timeout')))
 		$context['suggested_method'] = 'use_external_tool';
 	elseif ($zip_limit < $plain_limit && $messages < $zip_limit)
 		$context['suggested_method'] = 'zipped_file';

+ 2 - 4
Sources/ManageNews.php

@@ -1049,9 +1049,7 @@ function ModifyNewsSettings($return_config = false)
 	$context['page_title'] = $txt['admin_edit_news'] . ' - ' . $txt['settings'];
 	$context['sub_template'] = 'show_settings';
 
-	// Needed for the inline permission functions, and the settings template.
-	// @todo is this really needed?
-	require_once($sourcedir . '/ManagePermissions.php');
+	// Needed for the settings template.
 	require_once($sourcedir . '/ManageServer.php');
 
 	// Wrap it all up nice and warm...
@@ -1081,4 +1079,4 @@ function ModifyNewsSettings($return_config = false)
 	prepareDBSettingContext($config_vars);
 }
 
-?>
+?>

+ 4 - 1
Sources/ManageSmileys.php

@@ -323,6 +323,9 @@ function EditSmileySets()
 				}
 				$dir->close();
 
+				if (empty($smileys))
+					fatal_lang_error('smiley_set_dir_not_found', false, array($context['current_set']['name']));
+
 				// Exclude the smileys that are already in the database.
 				$request = $smcFunc['db_query']('', '
 					SELECT filename
@@ -1833,4 +1836,4 @@ function sortSmileyTable()
 	$smcFunc['db_remove_column']('{db_prefix}smileys', 'temp_order');
 }
 
-?>
+?>

+ 41 - 12
Sources/Packages.php

@@ -1377,23 +1377,33 @@ function PackageBrowse()
 				'function' => 'list_getPackages',
 				'params' => array('type' => $type, 'installed' => $installed),
 			),
+			'base_href' => $scripturl . '?action=admin;area=packages;sa=browse;type=' . $type,
+			'default_sort_col' => 'id' . $type,
 			'columns' => array(
-				'id' => array(
+				'id' . $type => array(
 					'header' => array(
-						'value' => '',
+						'value' => $txt['package_id'],
 						'style' => 'width: 32px;',
 					),
 					'data' => array(
-						'function' => create_function('$packages', '
-							static $packageCounter;
+						'function' => create_function('$package_md5', '
+							global $context;
+
+							if (isset($context[\'available_' . $type . '\'][$package_md5]))
+								return $context[\'available_' . $type . '\'][$package_md5][\'sort_id\'];
+							return $context[\'sort_id\'];
 							if (empty($packageCounter))
 								$packageCounter = 1;
 
 							return $packageCounter++ . \'.\';
 						'),
 					),
+					'sort' => array(
+						'default' => 'sort_id',
+						'reverse' => 'sort_id'
+					),
 				),
-				'mod_name' => array(
+				'mod_name' . $type => array(
 					'header' => array(
 						'value' => $txt['mod_name'],
 						'style' => 'width: 25%;',
@@ -1406,8 +1416,12 @@ function PackageBrowse()
 								return $context[\'available_' . $type . '\'][$package_md5][\'name\'];'
 						),
 					),
+					'sort' => array(
+						'default' => 'name',
+						'reverse' => 'name',
+					),
 				),
-				'version' => array(
+				'version' . $type => array(
 					'header' => array(
 						'value' => $txt['mod_version'],
 						'style' => 'width: 25%;',
@@ -1420,8 +1434,12 @@ function PackageBrowse()
 								return $context[\'available_' . $type . '\'][$package_md5][\'version\'];'
 						),
 					),
+					'sort' => array(
+						'default' => 'version',
+						'reverse' => 'version',
+					),
 				),
-				'operations' => array(
+				'operations' . $type => array(
 					'header' => array(
 						'value' => '',
 					),
@@ -1487,7 +1505,7 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 {
 	global $boarddir, $scripturl, $context, $forum_version;
 	static $instmods, $packages;
-	
+
 	// Start things up
 	if (!isset($packages[$params]))
 		$packages[$params] = array();
@@ -1553,6 +1571,7 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 	if ($dir = @opendir($boarddir . '/Packages'))
 	{
 		$dirs = array();
+		$sort_id = 1;
 		while ($package = readdir($dir))
 		{
 			if ($package == '.' || $package == '..' || $package == 'temp' || (!(is_dir($boarddir . '/Packages/' . $package) && file_exists($boarddir . '/Packages/' . $package . '/package-info.xml')) && substr(strtolower($package), -7) != '.tar.gz' && substr(strtolower($package), -4) != '.tgz' && substr(strtolower($package), -4) != '.zip'))
@@ -1594,6 +1613,7 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 			{
 				$packageInfo['installed_id'] = isset($installed_mods[$packageInfo['id']]) ? $installed_mods[$packageInfo['id']]['id'] : 0;
 
+				$packageInfo['sort_id'] = $sort_id++;
 				$packageInfo['is_installed'] = isset($installed_mods[$packageInfo['id']]);
 				$packageInfo['is_current'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] == $packageInfo['version']);
 				$packageInfo['is_newer'] = $packageInfo['is_installed'] && ($installed_mods[$packageInfo['id']]['version'] > $packageInfo['version']);
@@ -1681,31 +1701,40 @@ function list_getPackages($start, $items_per_page, $sort, $params, $installed)
 				// Modification.
 				if ($packageInfo['type'] == 'modification' || $packageInfo['type'] == 'mod')
 				{
-					$packages['modification'][] = md5($package);
+					$packages['modification'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_modification'][md5($package)] = $packageInfo;
 				}
 				// Avatar package.
 				elseif ($packageInfo['type'] == 'avatar')
 				{
-					$packages['avatar'][] = md5($package);
+					$packages['avatar'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_avatar'][md5($package)] = $packageInfo;
 				}
 				// Language package.
 				elseif ($packageInfo['type'] == 'language')
 				{
-					$packages['language'][] = md5($package);
+					$packages['language'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_language'][md5($package)] = $packageInfo;
 				}
 				// Other stuff.
 				else
 				{
-					$packages['unknown'][] = md5($package);
+					$packages['unknown'][strtolower($packageInfo[$sort])] = md5($package);
 					$context['available_unknown'][md5($package)] = $packageInfo;
 				}
 			}
 		}
 		closedir($dir);
 	}
+
+	if (isset($_GET['type']) && $_GET['type'] == $params)
+	{
+		if (isset($_GET['desc']))
+			krsort($packages[$params]);
+		else
+			ksort($packages[$params]);
+	}
+
 	return $packages[$params];
 }
 

+ 21 - 1
Sources/Security.php

@@ -754,10 +754,23 @@ function createToken($action, $type = 'post')
  */
 function validateToken($action, $type = 'post', $reset = true)
 {
-	global $modSettings;
+	global $modSettings, $sourcedir;
 
 	$type = $type == 'get' || $type == 'request' ? $type : 'post';
 
+	// Logins are special: the token is used to has the password with javascript before POST it
+	if ($action == 'login')
+	{
+		if (isset($_SESSION['token'][$type . '-' . $action]))
+		{
+			$return = $_SESSION['token'][$type . '-' . $action][3];
+			unset($_SESSION['token'][$type . '-' . $action]);
+			return $return;
+		}
+		else
+			return '';
+	}
+
 	// This nasty piece of code validates a token.
 	/*
 		1. The token exists in session.
@@ -783,6 +796,13 @@ function validateToken($action, $type = 'post', $reset = true)
 		// I'm back baby.
 		createToken($action, $type);
 
+		// Need to type in a password for that, man.
+		if (!isset($_GET['xml']))
+		{
+			require_once($sourcedir . '/Subs-Auth.php');
+			adminLogin($type, $action);
+		}
+
 		fatal_lang_error('token_verify_fail', false);
 	}
 	// Remove this token as its useless

+ 4 - 1
Sources/Subs-Auth.php

@@ -193,7 +193,7 @@ function InMaintenance()
  *
  * @param string $type = 'admin'
  */
-function adminLogin($type = 'admin')
+function adminLogin($type = 'admin', $additionalToken = false)
 {
 	global $context, $scripturl, $txt, $user_info, $user_settings;
 
@@ -230,6 +230,9 @@ function adminLogin($type = 'admin')
 	foreach ($_POST as $k => $v)
 		$context['post_data'] .= adminLogin_outputPostVars($k, $v);
 
+	if (!empty($additionalToken))
+		$context['post_data'] .= adminLogin_outputPostVars($context[$additionalToken . '_token_var'], $context[$additionalToken . '_token']);
+
 	// Now we'll use the admin_login sub template of the Login template.
 	$context['sub_template'] = 'admin_login';
 

+ 1 - 0
Sources/Subs-Post.php

@@ -1138,6 +1138,7 @@ function sendpm($recipients, $subject, $message, $store_outbox = false, $from =
 		'SUBJECT' => $subject,
 		'MESSAGE' => $message,
 		'SENDER' => un_htmlspecialchars($from['name']),
+		'READLINK' => $scripturl . '?action=pm;pmsg=' . $id_pm . '#msg' . $id_pm,
 		'REPLYLINK' => $scripturl . '?action=pm;sa=send;f=inbox;pmsg=' . $id_pm . ';quote;u=' . $from['id'],
 		'TOLIST' => implode(', ', $to_names),
 	);

+ 1 - 1
Sources/Subs.php

@@ -3700,7 +3700,7 @@ function setupMenuContext()
 	global $context, $modSettings, $user_info, $txt, $scripturl;
 
 	// Set up the menu privileges.
-	$context['allow_search'] = allowedTo('search_posts');
+	$context['allow_search'] = !empty($modSettings['allow_guestAccess']) ? allowedTo('search_posts') : (!$user_info['is_guest'] && allowedTo('search_posts'));
 	$context['allow_admin'] = allowedTo(array('admin_forum', 'manage_boards', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_attachments', 'manage_smileys'));
 	$context['allow_edit_profile'] = !$user_info['is_guest'] && allowedTo(array('profile_view_own', 'profile_view_any', 'profile_identity_own', 'profile_identity_any', 'profile_extra_own', 'profile_extra_any', 'profile_remove_own', 'profile_remove_any', 'moderate_forum', 'manage_membergroups', 'profile_title_own', 'profile_title_any'));
 	$context['allow_memberlist'] = allowedTo('view_mlist');

+ 19 - 7
Sources/Themes.php

@@ -1435,7 +1435,9 @@ function ThemeInstall()
 		if (!is_writable($boarddir . '/Themes'))
 			fatal_lang_error('theme_install_write_error', 'critical');
 
-		require_once($sourcedir . '/Subs-Package.php');
+		// This happens when the admin session is gone and the user has to login again
+		if (empty($_FILES['theme_gz']) && empty($_REQUEST['theme_gz']))
+			redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
 
 		// Set the default settings...
 		$theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.');
@@ -1470,6 +1472,15 @@ function ThemeInstall()
 		if (file_exists($theme_dir . '/theme_info.xml'))
 		{
 			$theme_info = file_get_contents($theme_dir . '/theme_info.xml');
+			// Parse theme-info.xml into an xmlArray.
+			require_once($sourcedir . '/Class-Package.php');
+			$theme_info_xml = new xmlArray($theme_info);
+			// @todo Error message of some sort?
+			if (!$theme_info_xml->exists('theme-info[0]'))
+				return 'package_get_error_packageinfo_corrupt';
+
+			$theme_info_xml = $theme_info_xml->path('theme-info[0]');
+			$theme_info_xml = $theme_info_xml->to_array();
 
 			$xml_elements = array(
 				'name' => 'name',
@@ -1479,17 +1490,18 @@ function ThemeInstall()
 			);
 			foreach ($xml_elements as $var => $name)
 			{
-				if (preg_match('~<' . $name . '>(?:<!\[CDATA\[)?(.+?)(?:\]\]>)?</' . $name . '>~', $theme_info, $match) == 1)
-					$install_info[$var] = $match[1];
+				if (!empty($theme_info_xml[$name]))
+					$install_info[$var] = $theme_info_xml[$name];
 			}
 
-			if (preg_match('~<images>(?:<!\[CDATA\[)?(.+?)(?:\]\]>)?</images>~', $theme_info, $match) == 1)
+			if (!empty($theme_info_xml['images']))
 			{
-				$install_info['images_url'] = $install_info['theme_url'] . '/' . $match[1];
+				$install_info['images_url'] = $install_info['theme_url'] . '/' . $theme_info_xml['images'];
 				$explicit_images = true;
 			}
-			if (preg_match('~<extra>(?:<!\[CDATA\[)?(.+?)(?:\]\]>)?</extra>~', $theme_info, $match) == 1)
-				$install_info += unserialize($match[1]);
+
+			if (!empty($theme_info_xml['extra']))
+				$install_info += unserialize($theme_info_xml['extra']);
 		}
 
 		if (isset($install_info['based_on']))

+ 4 - 4
Themes/default/Login.template.php

@@ -18,7 +18,7 @@ function template_login()
 	echo '
 		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 
-		<form action="', $scripturl, '?action=login2" name="frmLogin" id="frmLogin" method="post" accept-charset="', $context['character_set'], '" ', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\');"' : '', '>
+		<form action="', $scripturl, '?action=login2" name="frmLogin" id="frmLogin" method="post" accept-charset="', $context['character_set'], '" ', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
 		<div class="tborder login">
 			<div class="cat_bar">
 				<h3 class="catbg">
@@ -91,7 +91,7 @@ function template_kick_guest()
 	// This isn't that much... just like normal login but with a message at the top.
 	echo '
 	<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
-	<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\');"' : '', '>
+	<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
 		<div class="tborder login">
 			<div class="cat_bar">
 				<h3 class="catbg">', $txt['warning'], '</h3>
@@ -163,7 +163,7 @@ function template_maintenance()
 	// Display the administrator's message at the top.
 	echo '
 <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
-<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\');"' : '', '>
+<form action="', $scripturl, '?action=login2" method="post" accept-charset="', $context['character_set'], '"', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
 	<div class="tborder login" id="maintenance_mode">
 		<div class="cat_bar">
 			<h3 class="catbg">', $context['title'], '</h3>
@@ -204,7 +204,7 @@ function template_admin_login()
 	echo '
 <script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 
-<form action="', $scripturl, $context['get_data'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin" onsubmit="hash', ucfirst($context['sessionCheckType']), 'Password(this, \'', $context['user']['username'], '\', \'', $context['session_id'], '\');">
+<form action="', $scripturl, $context['get_data'], '" method="post" accept-charset="', $context['character_set'], '" name="frmLogin" id="frmLogin" onsubmit="hash', ucfirst($context['sessionCheckType']), 'Password(this, \'', $context['user']['username'], '\', \'', $context['session_id'], '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');">
 	<div class="tborder login" id="admin_login">
 		<div class="cat_bar">
 			<h3 class="catbg">

+ 1 - 1
Themes/default/Themes.template.php

@@ -161,7 +161,7 @@ function template_main()
 					</dl>
 					<hr class="hrcolor" />
 					<input type="submit" name="save" value="', $txt['theme_install_go'], '" class="button_submit" />
-					</br class="clear_right" />
+					<br class="clear_right" />
 				</div>
 			</div>
 			<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />

+ 6 - 0
Themes/default/languages/EmailTemplates.english.php

@@ -926,6 +926,7 @@ The following error occurred when processing a paid subscription
 	@additional_params: new_pm
 		SUBJECT: The personal message subject.
 		SENDER:  The user name for the member sending the personal message.
+		READLINK:  The link to directly access the read page.
 		REPLYLINK:  The link to directly access the reply page.
 	@description: A notification email sent to the receivers of a personal message
 */
@@ -934,6 +935,8 @@ $txt['new_pm_body'] = 'You have just been sent a personal message by {SENDER} on
 
 IMPORTANT: Remember, this is just a notification. Please do not reply to this email.
 
+Read this Personal Message here: {READLINK}
+
 Reply to this Personal Message here: {REPLYLINK}';
 
 /**
@@ -959,6 +962,7 @@ Reply to this Personal Message here: {REPLYLINK}';
 	@additional_params: new_pm_tolist
 		SUBJECT: The personal message subject.
 		SENDER:  The user name for the member sending the personal message.
+		READLINK:  The link to directly access the read page.
 		REPLYLINK:  The link to directly access the reply page.
 		TOLIST:  The list of users that will receive the personal message.
 	@description: A notification email sent to the receivers of a personal message
@@ -968,6 +972,8 @@ $txt['new_pm_tolist_body'] = 'You and {TOLIST} have just been sent a personal me
 
 IMPORTANT: Remember, this is just a notification. Please do not reply to this email.
 
+Read this Personal Message here: {READLINK}
+
 Reply to this Personal Message (to the sender only) here: {REPLYLINK}';
 
 /**

+ 2 - 1
Themes/default/languages/Errors.english.php

@@ -259,6 +259,7 @@ $txt['smiley_has_no_filename'] = 'No filename for this smiley was given.';
 $txt['smiley_not_unique'] = 'A smiley with that code already exists.';
 $txt['smiley_set_already_exists'] = 'A smiley set with that URL already exists';
 $txt['smiley_set_not_found'] = 'Smiley set not found';
+$txt['smiley_set_dir_not_found'] = 'The directory of the smiley set %1$s is either invalid or cannot be accessed';
 $txt['smiley_set_path_already_used'] = 'The URL of the smiley set is already being used by another smiley set.';
 $txt['smiley_set_unable_to_import'] = 'Unable to import smiley set. Either the directory is invalid or cannot be accessed.';
 
@@ -412,4 +413,4 @@ $txt['restore_not_found'] = 'The following messages could not be restored; the o
 $txt['error_invalid_dir'] = 'The directory you entered is invalid.';
 
 $txt['error_sqlite_optimizing'] = 'Sqlite is optimizing the database, the forum can not be accessed until it has finished.  Please try refreshing this page momentarily.';
-?>
+?>

+ 1 - 0
Themes/default/languages/Packages.english.php

@@ -4,6 +4,7 @@
 $txt['package_proceed'] = 'Proceed';
 $txt['php_script'] = 'Modification file was extracted, but this modification also comes with a PHP script which should be executed before it will work';
 $txt['package_run'] = 'Run';
+$txt['package_id'] = 'ID';
 $txt['package_read'] = 'Read';
 $txt['script_output'] = 'Script output:';
 $txt['additional_notes'] = 'Additional notes';

+ 6 - 6
Themes/default/scripts/script.js

@@ -666,7 +666,7 @@ function smf_avatarResize()
 }
 
 
-function hashLoginPassword(doForm, cur_session_id)
+function hashLoginPassword(doForm, cur_session_id, token)
 {
 	// Compatibility.
 	if (cur_session_id == null)
@@ -682,7 +682,7 @@ function hashLoginPassword(doForm, cur_session_id)
 	if (!('opera' in window))
 		doForm.passwrd.autocomplete = 'off';
 
-	doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id);
+	doForm.hash_passwrd.value = hex_sha1(hex_sha1(doForm.user.value.php_to8bit().php_strtolower() + doForm.passwrd.value.php_to8bit()) + cur_session_id + token);
 
 	// It looks nicer to fill it with asterisks, but Firefox will try to save that.
 	if (is_ff != -1)
@@ -691,7 +691,7 @@ function hashLoginPassword(doForm, cur_session_id)
 		doForm.passwrd.value = doForm.passwrd.value.replace(/./g, '*');
 }
 
-function hashAdminPassword(doForm, username, cur_session_id)
+function hashAdminPassword(doForm, username, cur_session_id, token)
 {
 	// Compatibility.
 	if (cur_session_id == null)
@@ -700,16 +700,16 @@ function hashAdminPassword(doForm, username, cur_session_id)
 	if (typeof(hex_sha1) == 'undefined')
 		return;
 
-	doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id);
+	doForm.admin_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.admin_pass.value.php_to8bit()) + cur_session_id + token);
 	doForm.admin_pass.value = doForm.admin_pass.value.replace(/./g, '*');
 }
 
-function hashModeratePassword(doForm, username, cur_session_id)
+function hashModeratePassword(doForm, username, cur_session_id, token)
 {
 	if (typeof(hex_sha1) == 'undefined')
 		return;
 
-	doForm.moderate_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.moderate_pass.value.php_to8bit()) + cur_session_id);
+	doForm.moderate_hash_pass.value = hex_sha1(hex_sha1(username.php_to8bit().php_strtolower() + doForm.moderate_pass.value.php_to8bit()) + cur_session_id + token);
 	doForm.moderate_pass.value = doForm.moderate_pass.value.replace(/./g, '*');
 }
 

+ 666 - 0
Themes/penguin/index.template.php

@@ -0,0 +1,666 @@
+<?php
+/**
+ * Simple Machines Forum (SMF)
+ *
+ * @package SMF
+ * @author Simple Machines
+ * @copyright 2011 Simple Machines
+ * @license http://www.simplemachines.org/about/smf/license.php BSD
+ *
+ * @version 2.1 Alpha 1
+ */
+
+/*	This template is, perhaps, the most important template in the theme. It
+	contains the main template layer that displays the header and footer of
+	the forum, namely with main_above and main_below. It also contains the
+	menu sub template, which appropriately displays the menu; the init sub
+	template, which is there to set the theme up; (init can be missing.) and
+	the linktree sub template, which sorts out the link tree.
+
+	The init sub template should load any data and set any hardcoded options.
+
+	The main_above sub template is what is shown above the main content, and
+	should contain anything that should be shown up there.
+
+	The main_below sub template, conversely, is shown after the main content.
+	It should probably contain the copyright statement and some other things.
+
+	The linktree sub template should display the link tree, using the data
+	in the $context['linktree'] variable.
+
+	The menu sub template should display all the relevant buttons the user
+	wants and or needs.
+
+	For more information on the templating system, please see the site at:
+	http://www.simplemachines.org/
+*/
+
+/**
+ * Initialize the template... mainly little settings.
+ */
+function template_init()
+{
+	global $context, $settings, $options, $txt;
+
+	/* Use images from default theme when using templates from the default theme?
+		if this is 'always', images from the default theme will be used.
+		if this is 'defaults', images from the default theme will only be used with default templates.
+		if this is 'never' or isn't set at all, images from the default theme will not be used. */
+	$settings['use_default_images'] = 'never';
+
+	/* What document type definition is being used? (for font size and other issues.)
+		'xhtml' for an XHTML 1.0 document type definition.
+		'html' for an HTML 4.01 document type definition. */
+	$settings['doctype'] = 'xhtml';
+
+	// The version this template/theme is for. This should probably be the version of SMF it was created for.
+	$settings['theme_version'] = '2.0';
+
+	// Set a setting that tells the theme that it can render the tabs.
+	$settings['use_tabs'] = true;
+
+	// Use plain buttons - as opposed to text buttons?
+	$settings['use_buttons'] = true;
+
+	// Show sticky and lock status separate from topic icons?
+	$settings['separate_sticky_lock'] = true;
+
+	// Does this theme use the strict doctype?
+	$settings['strict_doctype'] = false;
+
+	// Set the following variable to true if this theme requires the optional theme strings file to be loaded.
+	$settings['require_theme_strings'] = false;
+}
+
+/**
+ * The main sub template above the content.
+ */
+function template_html_above()
+{
+	global $context, $settings, $options, $scripturl, $txt, $modSettings;
+
+	// Show right to left and the character set for ease of translating.
+	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"', $context['right_to_left'] ? ' dir="rtl"' : '', '>
+<head>';
+
+	// The ?alp21 part of this link is just here to make sure browsers don't cache it wrongly.
+	echo '
+	<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/index', $context['theme_variant'], '.css?alp21" />';
+
+	// Some browsers need an extra stylesheet due to bugs/compatibility issues.
+	// Note: Commented this out as it will be unnecessary if we go ahead with setting browser as id on <body>.
+//	foreach (array('ie7', 'ie6', 'webkit') as $cssfix)
+//		if ($context['browser']['is_' . $cssfix])
+//			echo '
+//	<link rel="stylesheet" type="text/css" href="', $settings['default_theme_url'], '/css/', $cssfix, '.css" />';
+
+	// RTL languages require an additional stylesheet.
+	if ($context['right_to_left'])
+		echo '
+	<link rel="stylesheet" type="text/css" href="', $settings['theme_url'], '/css/rtl.css" />';
+
+	// load in any css from mods or themes so they can overwrite if wanted
+	template_css();
+
+	// Jquery Librarys
+	if (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'cdn')
+		echo '
+	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>';
+	elseif (isset($modSettings['jquery_source']) && $modSettings['jquery_source'] == 'local')
+		echo '
+	<script type="text/javascript" src="', $settings['theme_url'], '/scripts/jquery-1.7.1.min.js"></script>';
+	else
+		echo '
+	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
+	<script type="text/javascript"><!-- // --><![CDATA[
+		window.jQuery || document.write(\'<script src="', $settings['theme_url'], '/scripts/jquery-1.7.1.min.js"><\/script>\');
+	// ]]></script>';
+
+	// Note that the Superfish function seems to like being called by the full syntax.
+	// It doesn't appear to like being called by short syntax. Please test if contemplating changes.
+	echo '
+	<script type="text/javascript" src="', $settings['theme_url'], '/scripts/hoverIntent.js"></script>
+	<script type="text/javascript" src="', $settings['theme_url'], '/scripts/superfish.js"></script>
+	<script type="text/javascript" src="', $settings['theme_url'], '/scripts/jquery.bt.js"></script>';
+
+	// Here comes the JavaScript bits!
+	echo '
+	<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/script.js?alp21"></script>
+	<script type="text/javascript" src="', $settings['theme_url'], '/scripts/theme.js?alp21"></script>
+	<script type="text/javascript"><!-- // --><![CDATA[
+		var smf_theme_url = "', $settings['theme_url'], '";
+		var smf_default_theme_url = "', $settings['default_theme_url'], '";
+		var smf_images_url = "', $settings['images_url'], '";
+		var smf_scripturl = "', $scripturl, '";
+		var smf_iso_case_folding = ', $context['server']['iso_case_folding'] ? 'true' : 'false', ';
+		var smf_charset = "', $context['character_set'], '";
+		var smf_session_id = "', $context['session_id'], '";
+		var smf_session_var = "', $context['session_var'], '";
+		var smf_member_id = "', $context['user']['id'], '";', $context['show_pm_popup'] ? '
+		var fPmPopup = function ()
+		{
+			if (confirm("' . $txt['show_personal_messages'] . '"))
+				window.open(smf_prepareScriptUrl(smf_scripturl) + "action=pm");
+		}
+		addLoadEvent(fPmPopup);' : '', '
+		var ajax_notification_text = "', $txt['ajax_in_progress'], '";
+		var ajax_notification_cancel_text = "', $txt['modify_cancel'], '";
+	// ]]></script>';
+
+	echo '
+	<script type="text/javascript"><!-- // --><![CDATA[
+		$(document).ready(function() { 
+			$("ul.dropmenu, ul.quickbuttons, div.poster ul").superfish();
+		});
+	// ]]></script>';
+
+	// load in any javascript files from mods and themes
+	template_javascript();
+		
+	echo '
+	<meta http-equiv="Content-Type" content="text/html; charset=', $context['character_set'], '" />
+	<meta name="description" content="', $context['page_title_html_safe'], '" />', !empty($context['meta_keywords']) ? '
+	<meta name="keywords" content="' . $context['meta_keywords'] . '" />' : '', '
+	<title>', $context['page_title_html_safe'], '</title>';
+
+	// Please don't index these Mr Robot.
+	if (!empty($context['robot_no_index']))
+		echo '
+	<meta name="robots" content="noindex" />';
+
+	// Present a canonical url for search engines to prevent duplicate content in their indices.
+	if (!empty($context['canonical_url']))
+		echo '
+	<link rel="canonical" href="', $context['canonical_url'], '" />';
+
+	// Show all the relative links, such as help, search, contents, and the like.
+	echo '
+	<link rel="help" href="', $scripturl, '?action=help" />
+	<link rel="contents" href="', $scripturl, '" />', ($context['allow_search'] ? '
+	<link rel="search" href="' . $scripturl . '?action=search" />' : '');
+
+	// If RSS feeds are enabled, advertise the presence of one.
+	if (!empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged']))
+		echo '
+	<link rel="alternate" type="application/rss+xml" title="', $context['forum_name_html_safe'], ' - ', $txt['rss'], '" href="', $scripturl, '?type=rss2;action=.xml" />
+	<link rel="alternate" type="application/rss+xml" title="', $context['forum_name_html_safe'], ' - ', $txt['atom'], '" href="', $scripturl, '?type=atom;action=.xml" />';
+
+	// If we're viewing a topic, these should be the previous and next topics, respectively.
+	// Note: These have been modified to add additional functionality. Has already been tested live for two years.
+	// Please see http://www.simplemachines.org/community/index.php?topic=210212.msg2546739#msg2546739
+	// and
+	// http://www.simplemachines.org/community/index.php?topic=210212.msg2548628#msg2548628 for further details.
+	if (!empty($context['links']['next']))
+		echo '
+	<link rel="next" href="', $context['links']['next'], '" />';
+	else if (!empty($context['current_topic']))
+		echo '
+	<link rel="next" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=next" />';
+	if (!empty($context['links']['prev']))
+		echo '
+	<link rel="prev" href="', $context['links']['prev'], '" />';
+	else if (!empty($context['current_topic']))
+		echo '
+	<link rel="prev" href="', $scripturl, '?topic=', $context['current_topic'], '.0;prev_next=prev" />';
+
+	// If we're in a board, or a topic for that matter, the index will be the board's index.
+	if (!empty($context['current_board']))
+		echo '
+	<link rel="index" href="', $scripturl, '?board=', $context['current_board'], '.0" />';
+
+	// Output any remaining HTML headers. (from mods, maybe?)
+	echo $context['html_headers'];
+
+	echo '
+</head>
+<body id="', $context['browser_body_id'], '" class="action_', !empty($context['current_action']) ? htmlspecialchars($context['current_action']) : (!empty($context['current_board']) ? 'messageindex' : (!empty($context['current_topic']) ? 'display' : 'home')),
+	!empty($context['current_board']) ? ' board_' . htmlspecialchars($context['current_board']) : '',
+	'">';
+}
+
+function template_body_above()
+{
+	// Note: Globals updated to allow linking member avatar to their profile.
+	global $context, $settings, $options, $scripturl, $txt, $modSettings, $member;
+
+	// Note: Echo a true top-of-page link. This is much better than just having the standard SMF "Go up" and "Go down" links.
+	// Not that I have anything against going down, you understand. It's just that if one is going down one should do it properly.
+	echo '
+	<a id="top_most"></a>';
+
+	// Note: Header div is now full width.
+	// We could change this later to an HTML5 <header> tag if we use javascript to create some useful HTML5 elements for IE<9.
+	// Example javascript here: http://www.nickyeoman.com/blog/html/118-html5-tags-in-ie8
+	// Note: Wrapper has been changed from an id to a class, to enable better theming options, and the ability to set forum width on the theme settings page has been removed.
+	// Wrapper width is now set directly in index.css, as this is the only practical way of supporting an adaptable layout that will cope with mobile devices as well as desktop.
+
+	echo '
+
+	<div id="top_section">
+		<div class="wrapper">
+			<div class="frame">';
+
+	// Note: Markup past this point may be slightly WIP. Wear your armour-plated undies.
+	if ($context['user']['is_logged'])
+	{
+		if (!empty($context['user']['avatar']))
+	echo '
+				<a href="', $scripturl, '?action=profile" class="avatar">', $context['user']['avatar']['image'], '</a>';
+
+	echo '
+				<ul>
+					<li class="greeting">', $txt['hello_member_ndt'], ' <span>', $context['user']['name'], '</span>';
+	// Is the forum in maintenance mode?
+	if (($context['allow_moderation_center'] && ($context['in_maintenance'])||!empty ($context['unapproved_members'])||!empty($context['open_mod_reports'])))
+		{
+		echo '
+						<ul id="top_bar_notifications">';
+		if ($context['in_maintenance'])
+			echo '
+							<li class="notice">', $txt['maintain_mode_on'], '</li>';
+
+		if (!empty ($context['unapproved_members']))
+			echo '
+							<li>', $context['unapproved_members'] == 1 ? $txt['approve_thereis'] : $txt['approve_thereare'], ' <a href="', $scripturl, '?action=admin;area=viewmembers;sa=browse;type=approve" style="font-weight: bold;">', $context['unapproved_members'] == 1 ? $txt['approve_member'] : $context['unapproved_members'] . ' ' . $txt['approve_members'], ' ', $txt['approve_members_waiting'], '</a></li>';
+
+		if (!empty($context['open_mod_reports']))
+			echo '
+							<li><a href="', $scripturl, '?action=moderate;area=reports">', sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']), '</a></li>';
+
+			echo '
+						</ul>';
+		}
+		echo '
+					</li>
+					<li>', $context['current_time'], '</li>
+				</ul>';
+	}
+
+	// the upshrink image, right-floated
+	echo '
+				<img id="upshrink" src="', $settings['images_url'], '/upshrink.png" alt="*" title="', $txt['upshrink_description'], '" style="display: none;" />';
+
+	echo '
+				<form id="search_form" action="', $scripturl, '?action=search2" method="post" accept-charset="', $context['character_set'], '">
+					<input type="text" name="search" value="" class="input_text" />&nbsp;
+					<input type="submit" name="submit" value="', $txt['search'], '" class="button_submit" />
+					<input type="hidden" name="advanced" value="0" />';
+
+	// Search within current topic?
+	if (!empty($context['current_topic']))
+		echo '
+					<input type="hidden" name="topic" value="', $context['current_topic'], '" />';
+	// If we're on a certain board, limit it to this board ;).
+	elseif (!empty($context['current_board']))
+	echo '
+					<input type="hidden" name="brd[', $context['current_board'], ']" value="', $context['current_board'], '" />';
+
+	echo '
+				</form>';
+
+	echo '
+			</div>
+		</div>
+	</div>
+	<div id="header">
+		<div class="wrapper">
+			<div class="frame">
+				<h1 class="forumtitle">
+					<a href="', $scripturl, '">', empty($context['header_logo_url_html_safe']) ? $context['forum_name'] : '<img src="' . $context['header_logo_url_html_safe'] . '" alt="' . $context['forum_name'] . '" />', '</a>
+				</h1>
+
+	', empty($settings['site_slogan']) ? '<img id="smflogo" src="' . $settings['images_url'] . '/smflogo.png" alt="Simple Machines Forum" title="Simple Machines Forum" />' : '<div id="siteslogan">' . $settings['site_slogan'] . '</div>', '
+
+				<div id="upper_section" ', empty($options['collapse_header']) ? '' : ' style="display: none;"', '>
+					<div class="news" id="news_collapse">';
+
+	// Otherwise they're a guest - this time ask them to either register or login - lazy bums...
+	if (!empty($context['show_login_bar']))
+	{
+		echo '
+						<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
+						<form id="guest_form" style="float: left; margin-left: 0; width: auto;" action="', $scripturl, '?action=login2;quicklogin" method="post" accept-charset="', $context['character_set'], '" ', empty($context['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $context['session_id'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
+							<div class="info">', sprintf($txt[$context['can_register'] ? 'welcome_guest_register' : 'welcome_guest'], $txt['guest_title'], $scripturl . '?action=login'), '</div>
+							<input type="text" name="user" size="10" class="input_text" />
+							<input type="password" name="passwrd" size="10" class="input_password" />
+							<select name="cookielength">
+								<option value="60">', $txt['one_hour'], '</option>
+								<option value="1440">', $txt['one_day'], '</option>
+								<option value="10080">', $txt['one_week'], '</option>
+								<option value="43200">', $txt['one_month'], '</option>
+								<option value="-1" selected="selected">', $txt['forever'], '</option>
+							</select>
+							<input type="submit" value="', $txt['login'], '" class="button_submit" /><br />
+							<div class="info">', $txt['quick_login_dec'], '</div>';
+
+		if (!empty($modSettings['enableOpenID']))
+			echo '
+							<br /><input type="text" name="openid_identifier" size="25" class="input_text openid_login" />';
+
+		echo '
+							<input type="hidden" name="hash_passwrd" value="" />
+							<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+							<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '" />
+						</form>';
+	}
+
+	// Show a random news item? (or you could pick one from news_lines...)
+	if (!empty($settings['enable_news']))
+		echo '
+							<h2>', $txt['news'], ': </h2>
+							<p>', $context['random_news_line'], '</p>';
+
+	echo '
+					</div>
+				</div>';
+
+	// Define the upper_section toggle in JavaScript.
+	echo '
+		<script type="text/javascript"><!-- // --><![CDATA[
+			var oMainHeaderToggle = new smc_Toggle({
+				bToggleEnabled: true,
+				bCurrentlyCollapsed: ', empty($options['collapse_header']) ? 'false' : 'true', ',
+				aSwappableContainers: [
+					\'upper_section\'
+				],
+				aSwapImages: [
+					{
+						sId: \'upshrink\',
+						srcExpanded: smf_images_url + \'/upshrink.png\',
+						altExpanded: ', JavaScriptEscape($txt['upshrink_description']), ',
+						srcCollapsed: smf_images_url + \'/upshrink2.png\',
+						altCollapsed: ', JavaScriptEscape($txt['upshrink_description']), '
+					}
+				],
+				oThemeOptions: {
+					bUseThemeSettings: smf_member_id == 0 ? false : true,
+					sOptionName: \'collapse_header\',
+					sSessionVar: smf_session_var,
+					sSessionId: smf_session_id
+				},
+				oCookieOptions: {
+					bUseCookie: smf_member_id == 0 ? true : false,
+					sCookieName: \'upshrink\'
+				}
+			});
+		// ]]></script>';
+
+	// Show the menu here, according to the menu sub template.
+	template_menu();
+
+	echo '
+				<br class="clear" />
+			</div>
+		</div>
+	</div>';
+
+	// The main content should go here.
+	echo '
+	<div id="content_section">
+		<div class="wrapper">
+			<div class="frame">';
+
+	// Custom banners and shoutboxes should be placed here, before the linktree.
+
+	// Show the navigation tree.
+	theme_linktree();
+
+}
+
+function template_body_below()
+{
+	global $context, $settings, $options, $scripturl, $txt, $modSettings;
+
+	echo '
+			</div>
+		</div>
+	</div>';
+
+	// Show the "Powered by" and "Valid" logos, as well as the copyright. Remember, the copyright must be somewhere!
+	echo '
+	<div id="footer_section">
+		<div class="wrapper">
+			<div class="frame">
+				<ul>
+					<li class="copyright">', theme_copyright(), '</li>
+					<li><a id="button_xhtml" href="http://validator.w3.org/check?uri=referer" target="_blank" class="new_win" title="', $txt['valid_xhtml'], '"><span>', $txt['xhtml'], '</span></a></li>
+					', !empty($modSettings['xmlnews_enable']) && (!empty($modSettings['allow_guestAccess']) || $context['user']['is_logged']) ? '<li><a id="button_rss" href="' . $scripturl . '?action=.xml;type=rss" class="new_win"><span>' . $txt['rss'] . '</span></a></li>' : '', '
+					<li class="last"><a id="button_wap2" href="', $scripturl , '?wap2" class="new_win"><span>', $txt['wap2'], '</span></a></li>
+				</ul>
+				<a class="topmost" href="#top_most"></a>';
+
+	// Show the load time?
+	if ($context['show_load_time'])
+	echo '
+				<p>', $txt['page_created'], $context['load_time'], $txt['seconds_with'], $context['load_queries'], $txt['queries'], '</p>';
+
+	// Note: A bottom-of-page anchor has been inserted here to match the top-of-page anchor. 
+	echo '
+			</div>
+		</div>
+	</div><a id="bottom_most"></a>';
+}
+
+function template_html_below()
+{
+	global $context, $settings, $options, $scripturl, $txt, $modSettings;
+
+	template_javascript(true);
+
+	echo '
+</body></html>';
+}
+
+/**
+ * Show a linktree. This is that thing that shows "My Community | General Category | General Discussion"..
+ * @param bool $force_show = false
+ */
+function theme_linktree($force_show = false)
+{
+	global $context, $settings, $options, $shown_linktree, $txt, $scripturl;
+
+	// If linktree is empty, just return - also allow an override.
+	if (empty($context['linktree']) || (!empty($context['dont_default_linktree']) && !$force_show))
+		return;
+
+	echo '
+		<div class="navigate_section">
+			<ul>';
+
+	// Each tree item has a URL and name. Some may have extra_before and extra_after.
+	foreach ($context['linktree'] as $link_num => $tree)
+	{
+		echo '
+				<li', ($link_num == count($context['linktree']) - 1) ? ' class="last"' : '', '>';
+
+		// Show something before the link?
+		if (isset($tree['extra_before']))
+			echo $tree['extra_before'];
+
+		// Show the link, including a URL if it should have one.
+		echo $settings['linktree_link'] && isset($tree['url']) ? '
+					<a href="' . $tree['url'] . '"><span>' . $tree['name'] . '</span></a>' : '<span>' . $tree['name'] . '</span>';
+
+		// Show something after the link...?
+		if (isset($tree['extra_after']))
+			echo $tree['extra_after'];
+
+		// Don't show a separator for the last one.
+		if ($link_num != count($context['linktree']) - 1)
+			echo ' &#187;';
+
+		echo '
+				</li>';
+	}
+	echo '
+			</ul>';
+
+	if ($context['user']['is_logged'])
+	{
+	echo '
+			<ul class="unread_links">
+				<li><a href="', $scripturl, '?action=unread">'. $txt['unread_since_visit']. '</a>&nbsp;|</li>
+				<li><a href="', $scripturl, '?action=unreadreplies">'. $txt['show_unread_replies']. '</a></li>
+			</ul>';
+	}
+	echo '
+		</div>';
+
+	$shown_linktree = true;
+}
+
+/**
+ * Show the menu up top. Something like [home] [help] [profile] [logout]...
+ */
+function template_menu()
+{
+	global $context, $settings, $options, $scripturl, $txt;
+
+	echo '
+		<div id="main_menu">
+			<ul class="dropmenu" id="menu_nav">';
+
+	// Note: Menu markup has been cleaned up to remove unnecessary spans and classes. 
+	foreach ($context['menu_buttons'] as $act => $button)
+	{
+		echo '
+				<li id="button_', $act, '">
+					<a class="', $button['active_button'] ? 'active' : '', !empty($button['is_last']) ? ' last' : '', '" href="', $button['href'], '"', isset($button['target']) ? ' target="' . $button['target'] . '"' : '', '>
+						', $button['title'], '
+					</a>';
+		if (!empty($button['sub_buttons']))
+		{
+			echo '
+					<ul>';
+
+			foreach ($button['sub_buttons'] as $childbutton)
+			{
+				echo '
+						<li>
+							<a href="', $childbutton['href'], '" ', isset($childbutton['is_last']) ? 'class="last"' : '' , isset($childbutton['target']) ? ' target="' . $childbutton['target'] . '"' : '', '>
+								', $childbutton['title'], '
+							</a>';
+				// 3rd level menus :)
+				if (!empty($childbutton['sub_buttons']))
+				{
+					echo '
+							<ul>';
+
+					foreach ($childbutton['sub_buttons'] as $grandchildbutton)
+						echo '
+								<li>
+									<a href="', $grandchildbutton['href'], '" ', isset($grandchildbutton['is_last']) ? ' class="last"' : '' , isset($grandchildbutton['target']) ? ' target="' . $grandchildbutton['target'] . '"' : '', '>
+										', $grandchildbutton['title'], '
+									</a>
+								</li>';
+
+					echo '
+							</ul>';
+				}
+
+				echo '
+						</li>';
+			}
+				echo '
+					</ul>';
+		}
+		echo '
+				</li>';
+	}
+
+	echo '
+			</ul>
+		</div>';
+}
+
+/**
+ * Generate a strip of buttons.
+ * @param array $button_strip
+ * @param string $direction = ''
+ * @param array $strip_options = array()
+ */
+function template_button_strip($button_strip, $direction = '', $strip_options = array())
+{
+	global $settings, $context, $txt, $scripturl;
+
+	if (!is_array($strip_options))
+		$strip_options = array();
+
+	// List the buttons in reverse order for RTL languages.
+	if ($context['right_to_left'])
+		$button_strip = array_reverse($button_strip, true);
+
+	// Create the buttons...
+	$buttons = array();
+	foreach ($button_strip as $key => $value)
+	{
+		if (!isset($value['test']) || !empty($context[$value['test']]))
+			$buttons[] = '
+				<li><a' . (isset($value['id']) ? ' id="button_strip_' . $value['id'] . '"' : '') . ' class="button_strip_' . $key . (isset($value['active']) ? ' active' : '') . '" href="' . $value['url'] . '"' . (isset($value['custom']) ? ' ' . $value['custom'] : '') . '><span>' . $txt[$value['text']] . '</span></a></li>';
+	}
+
+	// No buttons? No button strip either.
+	if (empty($buttons))
+		return;
+
+	// Can we have a first one too?.
+	$buttons[0] = str_replace('" href="', ' first" href="', $buttons[0]);
+
+	// Make the last one, as easy as possible.
+	$buttons[count($buttons) - 1] = str_replace('" href="', ' last" href="', $buttons[count($buttons) - 1]);
+
+	echo '
+		<div class="buttonlist', !empty($direction) ? ' float' . $direction : '', '"', (empty($buttons) ? ' style="display: none;"' : ''), (!empty($strip_options['id']) ? ' id="' . $strip_options['id'] . '"': ''), '>
+			<ul>',
+				implode('', $buttons), '
+			</ul>
+		</div>';
+}
+
+/**
+ * Output the Javascript files
+ */
+function template_javascript($do_defered = false)
+{
+	global $context;
+
+	// Use this hook to minify/optimize Javascript files
+	call_integration_hook('pre_javascript_output');
+
+	foreach ($context['javascript_files'] as $filename => $options)
+		if ((!$do_defered && empty($options['defer'])) || ($do_defered && !empty($options['defer'])))
+			echo '
+		<script type="text/javascript" src="', $filename, '"></script>';
+}
+
+/**
+ * Output the Javascript vars
+ */
+function template_javascript_vars()
+{
+	global $context;
+
+	call_integration_hook('pre_javascript_vars_output');
+
+	foreach ($context['javascript_vars'] as $key => $value)
+		echo '
+		var ', $key, ' = ', $value;
+}
+
+/**
+ * Output the CSS files
+ */
+function template_css()
+{
+	global $context;
+
+	// Use this hook to minify/optimize CSS files
+	call_integration_hook('pre_css_output');
+
+	foreach ($context['css_files'] as $filename => $options)
+		echo '
+	<link rel="stylesheet" type="text/css" href="', $filename, '" />';
+}
+
+?>

+ 1 - 1
other/install.php

@@ -33,7 +33,7 @@ $databases = array(
 		'utf8_support' => true,
 		'utf8_version' => '4.1.0',
 		'utf8_version_check' => 'return mysql_get_server_info();',
-		'utf8_default' => false,
+		'utf8_default' => true,
 		'utf8_required' => false,
 		'alter_support' => true,
 		'validate_prefix' => create_function('&$value', '

+ 6 - 2
other/upgrade.php

@@ -13,7 +13,7 @@
 
 // Version information...
 define('SMF_VERSION', '2.1 Alpha 1');
-define('SMF_LANG_VERSION', '2.0');
+define('SMF_LANG_VERSION', '2.1');
 
 $GLOBALS['required_php_version'] = '5.1.0';
 $GLOBALS['required_mysql_version'] = '4.0.18';
@@ -984,6 +984,9 @@ function WelcomeLogin()
 		$boarddir . '/Settings_bak.php',
 	);
 
+	require_once($sourcedir . '/Security.php');
+	createToken('login');
+
 	// Check the cache directory.
 	$cachedir_temp = empty($cachedir) ? $boarddir . '/cache' : $cachedir;
 	if (!file_exists($cachedir_temp))
@@ -3639,7 +3642,8 @@ function template_welcome_message()
 		<script type="text/javascript" src="http://www.simplemachines.org/smf/current-version.js?version=' . SMF_VERSION . '"></script>
 		<script type="text/javascript" src="', $settings['default_theme_url'], '/scripts/sha1.js"></script>
 			<h3>', sprintf($txt['upgrade_ready_proceed'], SMF_VERSION), '</h3>
-	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform" ', empty($upcontext['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $upcontext['rid'] . '\');"' : '', '>
+	<form action="', $upcontext['form_url'], '" method="post" name="upform" id="upform" ', empty($upcontext['disable_login_hashing']) ? ' onsubmit="hashLoginPassword(this, \'' . $upcontext['rid'] . '\', \'' . (!empty($context['login_token']) ? $context['login_token'] : '') . '\');"' : '', '>
+		<input type="hidden" name="', $context['login_token_var'], '" value="', $context['login_token'], '" />
 		<div id="version_warning" style="margin: 2ex; padding: 2ex; border: 2px dashed #a92174; color: black; background-color: #fbbbe2; display: none;">
 			<div style="float: left; width: 2ex; font-size: 2em; color: red;">!!</div>
 			<strong style="text-decoration: underline;">', $txt['upgrade_warning'], '</strong><br />