Просмотр исходного кода

Merge pull request #817 from MissAllSunday/version_check_for_themes

Lets bork something!
Jessica González 11 лет назад
Родитель
Сommit
b3eebfdebc

+ 112 - 19
Sources/Themes.php

@@ -1288,7 +1288,8 @@ function PickTheme()
  */
 function ThemeInstall()
 {
-	global $sourcedir, $boarddir, $boardurl, $txt, $context, $settings, $modSettings, $smcFunc;
+	global $sourcedir, $boarddir, $boardurl, $txt, $context;
+	global $settings, $modSettings, $scripturl, $smcFunc, $forum_version;
 
 	checkSession('request');
 
@@ -1298,6 +1299,10 @@ function ThemeInstall()
 	require_once($sourcedir . '/Subs-Package.php');
 
 	loadTemplate('Themes');
+	loadLanguage('Errors');
+
+	// Make it easier to change the path.
+	$themedir = $boarddir . '/Themes';
 
 	if (isset($_GET['theme_id']))
 	{
@@ -1329,7 +1334,7 @@ function ThemeInstall()
 
 	if ((!empty($_FILES['theme_gz']) && (!isset($_FILES['theme_gz']['error']) || $_FILES['theme_gz']['error'] != 4)) || !empty($_REQUEST['theme_gz']))
 		$method = 'upload';
-	elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath($boarddir . '/Themes') && file_exists($_REQUEST['theme_dir']))
+	elseif (isset($_REQUEST['theme_dir']) && rtrim(realpath($_REQUEST['theme_dir']), '/\\') != realpath($themedir) && file_exists($_REQUEST['theme_dir']))
 		$method = 'path';
 	else
 		$method = 'copy';
@@ -1337,10 +1342,10 @@ function ThemeInstall()
 	if (!empty($_REQUEST['copy']) && $method == 'copy')
 	{
 		// Hopefully the themes directory is writable, or we might have a problem.
-		if (!is_writable($boarddir . '/Themes'))
+		if (!is_writable($themedir))
 			fatal_lang_error('theme_install_write_error', 'critical');
 
-		$theme_dir = $boarddir . '/Themes/' . preg_replace('~[^A-Za-z0-9_\- ]~', '', $_REQUEST['copy']);
+		$theme_dir = $themedir . '/' . preg_replace('~[^A-Za-z0-9_\- ]~', '', $_REQUEST['copy']);
 
 		umask(0);
 		mkdir($theme_dir, 0777);
@@ -1366,7 +1371,7 @@ function ThemeInstall()
 		package_flush_cache();
 
 		$theme_name = $_REQUEST['copy'];
-		$images_url = $boardurl . '/Themes/' . basename($theme_dir) . '/images';
+		$images_url = $themedir . '/' . basename($theme_dir) . '/images';
 		$theme_dir = realpath($theme_dir);
 
 		// Lets get some data for the new theme.
@@ -1398,8 +1403,11 @@ function ThemeInstall()
 		$xml_info = '<' . '?xml version="1.0"?' . '>
 <theme-info xmlns="http://www.simplemachines.org/xml/theme-info" xmlns:smf="http://www.simplemachines.org/">
 	<!-- For the id, always use something unique - put your name, a colon, and then the package name. -->
-	<id>smf:' . $smcFunc['strtolower'](str_replace(array(' '), '_', $_REQUEST['copy'])) . '</id>
-	<version>' . $modSettings['smfVersion'] . '</version>
+	<id>smf:' . $smcFunc['strtolower'](trim(str_replace(array(' '), '_', $_REQUEST['copy']))) . '</id>
+	<!-- The theme\'s version, please try to use semantic versioning. -->
+	<version>1.0</version>
+	<!-- Install for, the SMF versions this theme was designed for. Uses the same wildcards used in the packager manager. This field is mandatory. -->
+	<install for="2.1 - 2.1.99, '. strtr($forum_version, array('SMF ' => '')) .'" />
 	<!-- Theme name, used purely for aesthetics. -->
 	<name>' . $_REQUEST['copy'] . '</name>
 	<!-- Author: your email address or contact information. The name attribute is optional. -->
@@ -1422,6 +1430,7 @@ function ThemeInstall()
 			fclose($fp);
 		}
 	}
+
 	elseif (isset($_REQUEST['theme_dir']) && $method == 'path')
 	{
 		if (!is_dir($_REQUEST['theme_dir']) || !file_exists($_REQUEST['theme_dir'] . '/theme_info.xml'))
@@ -1430,10 +1439,11 @@ function ThemeInstall()
 		$theme_name = basename($_REQUEST['theme_dir']);
 		$theme_dir = $_REQUEST['theme_dir'];
 	}
-	elseif ($method = 'upload')
+
+	elseif ($method == 'upload')
 	{
 		// Hopefully the themes directory is writable, or we might have a problem.
-		if (!is_writable($boarddir . '/Themes'))
+		if (!is_writable($themedir))
 			fatal_lang_error('theme_install_write_error', 'critical');
 
 		// This happens when the admin session is gone and the user has to login again
@@ -1443,42 +1453,65 @@ function ThemeInstall()
 		// Set the default settings...
 		$theme_name = strtok(basename(isset($_FILES['theme_gz']) ? $_FILES['theme_gz']['name'] : $_REQUEST['theme_gz']), '.');
 		$theme_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $theme_name);
-		$theme_dir = $boarddir . '/Themes/' . $theme_name;
+		$theme_dir = $themedir . '/' . $theme_name;
 
 		if (isset($_FILES['theme_gz']) && is_uploaded_file($_FILES['theme_gz']['tmp_name']) && (ini_get('open_basedir') != '' || file_exists($_FILES['theme_gz']['tmp_name'])))
-			$extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $boarddir . '/Themes/' . $theme_name, false, true);
+			$extracted = read_tgz_file($_FILES['theme_gz']['tmp_name'], $themedir . '/' . $theme_name, false, true);
+
 		elseif (isset($_REQUEST['theme_gz']))
 		{
 			// Check that the theme is from simplemachines.org, for now... maybe add mirroring later.
 			if (preg_match('~^http://[\w_\-]+\.simplemachines\.org/~', $_REQUEST['theme_gz']) == 0 || strpos($_REQUEST['theme_gz'], 'dlattach') !== false)
 				fatal_lang_error('not_on_simplemachines');
 
-			$extracted = read_tgz_file($_REQUEST['theme_gz'], $boarddir . '/Themes/' . $theme_name, false, true);
+			$extracted = read_tgz_file($_REQUEST['theme_gz'], $themedir . '/' . $theme_name, false, true);
 		}
 		else
 			redirectexit('action=admin;area=theme;sa=admin;' . $context['session_var'] . '=' . $context['session_id']);
 	}
 
-	// Something go wrong?
+	// Let us proceed with the install.
 	if ($theme_dir != '' && basename($theme_dir) != 'Themes')
 	{
 		// Defaults.
 		$install_info = array(
 			'theme_url' => $boardurl . '/Themes/' . basename($theme_dir),
-			'images_url' => isset($images_url) ? $images_url : $boardurl . '/Themes/' . basename($theme_dir) . '/images',
+			'images_url' => isset($images_url) ? $images_url : $themedir . '/' . basename($theme_dir) . '/images',
 			'theme_dir' => $theme_dir,
 			'name' => $theme_name
 		);
 
-		if (file_exists($theme_dir . '/theme_info.xml'))
+		// Perhaps they are trying to install a mod, lets tell them nicely this is the wrong function.
+		if (file_exists($theme_dir . '/package-info.xml'))
+		{
+			$txt['package_get_error_is_mod'] = str_replace('{MANAGEMODURL}', $scripturl . '?action=admin;area=packages;' . $context['session_var'] . '=' . $context['session_id'], $txt['package_get_error_is_mod']);
+			fatal_lang_error('package_theme_upload_error_broken', false, $txt['package_get_error_is_mod']);
+		}
+
+		// Get the theme info.
+		elseif (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?
+
+			// Error message, there isn't any valid info.
 			if (!$theme_info_xml->exists('theme-info[0]'))
-				return 'package_get_error_packageinfo_corrupt';
+				fatal_lang_error('package_get_error_packageinfo_corrupt', false);
+
+			// Check for compatibility with 2.1 or greater.
+			if (!$theme_info_xml->exists('theme-info/install'))
+				fatal_lang_error('package_get_error_theme_not_compatible', false, $forum_version);
+
+			// So, we have an install tag which is cool and stuff but we also need to check it and match your current SMF version...
+			$the_version = strtr($forum_version, array('SMF ' => ''));
+			$install_versions = $theme_info_xml->path('theme-info/install/@for');
+
+			// The theme isn't compatible with the current SMF version.
+			if (!$install_versions || !matchPackageVersion($the_version, $install_versions))
+				fatal_lang_error('package_get_error_theme_not_compatible', false, $forum_version);
 
 			$theme_info_xml = $theme_info_xml->path('theme-info[0]');
 			$theme_info_xml = $theme_info_xml->to_array();
@@ -1488,11 +1521,64 @@ function ThemeInstall()
 				'theme_layers' => 'layers',
 				'theme_templates' => 'templates',
 				'based_on' => 'based-on',
+				'version' => 'version',
 			);
+
+			// Assign the values to be stored.
 			foreach ($xml_elements as $var => $name)
-			{
 				if (!empty($theme_info_xml[$name]))
 					$install_info[$var] = $theme_info_xml[$name];
+
+			// OK, is this a newer version of an already installed theme?
+			if (!empty($install_info['version']))
+			{
+				$to_update = array();
+				$request = $smcFunc['db_query']('', '
+					SELECT th.value AS name, th.id_theme, th2.value AS version
+					FROM {db_prefix}themes AS th
+						INNER JOIN {db_prefix}themes AS th2 ON (th2.id_theme = th.id_theme
+							AND th2.id_member = {int:no_member}
+							AND th2.variable = {string:version})
+					WHERE th.id_member = {int:no_member}
+						AND th.variable = {string:name}
+						AND th.value LIKE {string:name_value}
+					LIMIT 1',
+					array(
+						'no_member' => 0,
+						'name' => 'name',
+						'version' => 'version',
+						'name_value' => '%'. $install_info['name'] .'%',
+					)
+				);
+				$to_update = $smcFunc['db_fetch_assoc']($request);
+				$smcFunc['db_free_result']($request);
+
+				// Got something, lets figure it out what to do next.
+				if (!empty($to_update) && !empty($to_update['version']))
+					switch (compareVersions($install_info['version'], $to_update['version']))
+					{
+						case 0: // This is exactly the same theme.
+						case -1: // The one being installed is older than the one already installed.
+						default: // Any other possible result.
+							fatal_lang_error('package_get_error_theme_no_new_version', false, array($install_info['version'], $to_update['version']));
+							break;
+						case 1: // Got a newer version, update the old entry.
+							$smcFunc['db_query']('', '
+								UPDATE {db_prefix}themes
+								SET value = {string:new_value}
+								WHERE variable = {string:version}
+									AND id_theme = {int:id_theme}',
+								array(
+									'new_value' => $install_info['version'],
+									'version' => 'version',
+									'id_theme' => $to_update['id_theme'],
+								)
+							);
+
+							// Do a redirect and set a nice updated message.
+							redirectexit('action=admin;area=theme;sa=install;theme_id=' . $to_update['id_theme'] . ';updated;' . $context['session_var'] . '=' . $context['session_id']);
+							break;
+					}
 			}
 
 			if (!empty($theme_info_xml['images']))
@@ -1507,11 +1593,14 @@ function ThemeInstall()
 
 		if (isset($install_info['based_on']))
 		{
+			// No need for elaborated stuff when the theme is based on the default one.
 			if ($install_info['based_on'] == 'default')
 			{
 				$install_info['theme_url'] = $settings['default_theme_url'];
 				$install_info['images_url'] = $settings['default_images_url'];
 			}
+
+			// Custom theme based on another custom theme, lets get some info.
 			elseif ($install_info['based_on'] != '')
 			{
 				$install_info['based_on'] = preg_replace('~[^A-Za-z0-9\-_ ]~', '', $install_info['based_on']);
@@ -1541,7 +1630,7 @@ function ThemeInstall()
 				$temp = $smcFunc['db_fetch_assoc']($request);
 				$smcFunc['db_free_result']($request);
 
-				// @todo An error otherwise?
+				// Found the based on theme info, add it to the current one being installed.
 				if (is_array($temp))
 				{
 					$install_info = $temp + $install_info;
@@ -1549,6 +1638,10 @@ function ThemeInstall()
 					if (empty($explicit_images) && !empty($install_info['base_theme_url']))
 						$install_info['theme_url'] = $install_info['base_theme_url'];
 				}
+
+				// Nope, sorry, couldn't find any theme already installed.
+				else
+					fatal_lang_error('package_get_error_theme_no_based_on_found', false, $install_info['based_on']);
 			}
 
 			unset($install_info['based_on']);

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

@@ -745,7 +745,7 @@ function template_installed()
 		<div class="windowbg">
 			<div class="content">
 				<p>
-					<a href="', $scripturl, '?action=admin;area=theme;sa=list;th=', $context['installed_theme']['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $context['installed_theme']['name'], '</a> ', $txt['theme_installed_message'], '
+					<a href="', $scripturl, '?action=admin;area=theme;sa=list;th=', $context['installed_theme']['id'], ';', $context['session_var'], '=', $context['session_id'], '">', $context['installed_theme']['name'], '</a> ', $txt['theme_'. (isset($_GET['updated']) ? 'updated' : 'installed') .'_message'], '
 				</p>
 				<p>
 					<a href="', $scripturl, '?action=admin;area=theme;sa=admin;', $context['session_var'], '=', $context['session_id'], '">', $txt['back'], '</a>

+ 5 - 0
Themes/default/languages/Errors.english.php

@@ -294,12 +294,17 @@ $txt['package_upload_error_failed'] = 'Could not upload package, please check di
 $txt['package_upload_error_exists'] = 'The file you are uploading already exists on the server. Please delete it first then try again.';
 $txt['package_upload_error_supports'] = 'The package manager currently allows only these file types: %1$s.';
 $txt['package_upload_error_broken'] = 'Package upload failed due to the following error:<br />&quot;%1$s&quot;';
+$txt['package_theme_upload_error_broken'] = 'Theme upload failed due to the following error:<br />&quot;%1$s&quot;';
 
 $txt['package_get_error_not_found'] = 'The package you are trying to install cannot be located. You may want to manually upload the package to your Packages directory.';
 $txt['package_get_error_missing_xml'] = 'The package you are attempting to install is missing the package-info.xml that must be in the root package directory.';
 $txt['package_get_error_is_zero'] = 'Although the package was downloaded to the server it appears to be empty. Please check the Packages directory, and the &quot;temp&quot; sub-directory are both writable. If you continue to experience this problem you should try extracting the package on your PC and uploading the extracted files into a subdirectory in your Packages directory and try again. For example, if the package was called shout.tar.gz you should:<br />1) Download the package to your local PC and extract it into files.<br />2) Using an FTP client create a new directory in your &quot;Packages&quot; folder, in this example you may call it "shout".<br />3) Upload all the files from the extracted package to this directory.<br />4) Go back to the package manager browse page and the package will be automatically found by SMF.';
 $txt['package_get_error_packageinfo_corrupt'] = 'SMF was unable to find any valid information within the package-info.xml file included within the Package. There may be an error with the modification, or the package may be corrupt.';
 $txt['package_get_error_is_theme'] = 'You can\'t install a Theme from this section, please use the <a href="{MANAGETHEMEURL}">Themes and Layout</a> management page to upload it';
+$txt['package_get_error_is_mod'] = 'You can\'t install a mod from this section, please use the <a href="{MANAGEMODURL}">Package manager</a> page to upload it';
+$txt['package_get_error_theme_not_compatible'] = 'Your theme does not show it has compatibility with %1$s. Please contact the theme author.';
+$txt['package_get_error_theme_no_based_on_found'] = 'The theme you\'re trying to install depends on another theme: %1$s, you need to install that theme first.';
+$txt['package_get_error_theme_no_new_version'] = 'The theme you\'re trying to install is already installed or is an outdated version of it. The version you\'re trying to install is: %1$s and the version already installed is: %2$s.';
 
 $txt['no_membergroup_selected'] = 'No membergroup selected';
 $txt['membergroup_does_not_exist'] = 'The membergroup doesn\'t exist or is invalid.';

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

@@ -24,6 +24,7 @@ $txt['theme_install_new_confirm'] = 'Are you sure you want to install this new t
 $txt['theme_install_writable'] = 'Warning - you cannot create or install a new theme as your themes directory is not currently writable.';
 $txt['theme_installed'] = 'Installed Successfully';
 $txt['theme_installed_message'] = 'was installed successfully.';
+$txt['theme_updated_message'] = 'was updated successfully.';
 
 $txt['theme_latest'] = 'Latest and Greatest Themes';
 $txt['theme_latest_fetch'] = 'Fetching latest themes from www.simplemachines.org...';