Browse Source

Merge pull request #68 from emanuele45/alter_to_maintenance

Alter columnss is a job for maintenace tasks, not for settings change
emanuele45 12 years ago
parent
commit
62fab1b749

+ 156 - 0
Sources/ManageMaintenance.php

@@ -64,6 +64,7 @@ function ManageMaintenance()
 				'backup' => 'MaintainDownloadBackup',
 				'convertentities' => 'ConvertEntities',
 				'convertutf8' => 'ConvertUtf8',
+				'convertmsgbody' => 'ConvertMsgBody',
 			),
 		),
 		'members' => array(
@@ -132,6 +133,19 @@ function MaintainDatabase()
 	$context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']()), '<=');
 	$context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8';
 
+	if ($db_type == 'mysql')
+	{
+		db_extend('packages');
+
+		$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+		foreach ($colData as $column)
+			if ($column['name'] == 'body')
+				$body_type = $column['type'];
+
+		$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+		$context['convert_to_suggest'] = ($body_type != 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536);
+	}
+
 	// Check few things to give advices before make a backup
 	// If safe mod is enable the external tool is *always* the best (and probably the only) solution
 	$context['safe_mode_enable'] = @ini_get('safe_mode');
@@ -733,6 +747,148 @@ function ConvertUtf8()
 	redirectexit('action=admin;area=maintain;done=convertutf8');
 }
 
+/**
+ * Convert the column "body" of the table {db_prefix}messages from TEXT to MEDIUMTEXT and vice versa.
+ * It requires the admin_forum permission.
+ * This is needed only for MySQL.
+ * During the convertion from MEDIUMTEXT to TEXT it check if any of the posts exceed the TEXT length and if so it aborts.
+ * This action is linked from the maintenance screen (if it's applicable).
+ * Accessed by ?action=admin;area=maintain;sa=database;activity=convertmsgbody.
+ *
+ * @uses the convert_msgbody sub template of the Admin template.
+ */
+function ConvertMsgBody()
+{
+	global $scripturl, $context, $txt, $language, $db_character_set, $db_type;
+	global $modSettings, $user_info, $sourcedir, $smcFunc, $db_prefix, $time_start;
+
+	// Show me your badge!
+	isAllowedTo('admin_forum');
+
+	if ($db_type != 'mysql')
+		return;
+
+	db_extend('packages');
+
+	$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+	foreach ($colData as $column)
+		if ($column['name'] == 'body')
+			$body_type = $column['type'];
+
+	$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+
+	if ($body_type == 'text' || ($body_type != 'text' && isset($_POST['do_conversion'])))
+	{
+		checkSession();
+		validateToken('admin-maint');
+
+		// Make it longer so we can do their limit.
+		if ($body_type == 'text')
+			$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext'));
+		// Shorten the column so we can have a bit (literally per record) less space occupied
+		else
+			$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text'));
+
+		$colData = $smcFunc['db_list_columns']('{db_prefix}messages', true);
+		foreach ($colData as $column)
+			if ($column['name'] == 'body')
+				$body_type = $column['type'];
+
+		$context['maintenance_finished'] = $txt[$context['convert_to'] . '_title'];
+		$context['convert_to'] = $body_type == 'text' ? 'mediumtext' : 'text';
+		$context['convert_to_suggest'] = ($body_type != 'text' && !empty($modSettings['max_messageLength']) && $modSettings['max_messageLength'] < 65536);
+
+		return;
+		redirectexit('action=admin;area=maintain;sa=database');
+	}
+	elseif ($body_type != 'text' && (!isset($_POST['do_conversion']) || isset($_POST['cont'])))
+	{
+		checkSession();
+		if (empty($_REQUEST['start']))
+			validateToken('admin-maint');
+		else
+			validateToken('admin-convertMsg');
+
+		$context['page_title'] = $txt['not_done_title'];
+		$context['continue_post_data'] = '';
+		$context['continue_countdown'] = 3;
+		$context['sub_template'] = 'not_done';
+		$increment = 500;
+		$id_msg_exceeding = isset($_POST['id_msg_exceeding']) ? explode(',', $_POST['id_msg_exceeding']) : array();
+
+		$request = $smcFunc['db_query']('', '
+			SELECT COUNT(*) as count
+			FROM {db_prefix}messages',
+			array()
+		);
+		list($max_msgs) = $smcFunc['db_fetch_row']($request);
+		$smcFunc['db_free_result']($request);
+
+		// Try for as much time as possible.
+		@set_time_limit(600);
+
+		while ($_REQUEST['start'] < $max_msgs)
+		{
+			$request = $smcFunc['db_query']('', '
+				SELECT /*!40001 SQL_NO_CACHE */ id_msg
+				FROM {db_prefix}messages
+				WHERE id_msg BETWEEN {int:start} AND {int:start} + {int:increment}
+					AND LENGTH(body) > 65535',
+				array(
+					'start' => $_REQUEST['start'],
+					'increment' => $increment - 1,
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+				$id_msg_exceeding[] = $row['id_msg'];
+			$smcFunc['db_free_result']($request);
+
+			$_REQUEST['start'] += $increment;
+
+			if (array_sum(explode(' ', microtime())) - array_sum(explode(' ', $time_start)) > 3)
+			{
+				createToken('admin-convertMsg');
+				$context['continue_post_data'] = '
+					<input type="hidden" name="' . $context['admin-convertMsg_token_var'] . '" value="' . $context['admin-convertMsg_token'] . '" />
+					<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
+					<input type="hidden" name="id_msg_exceeding" value="' . implode(',', $id_msg_exceeding) . '" />';
+
+				$context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertmsgbody;start=' . $_REQUEST['start'];
+				$context['continue_percent'] = round(100 * $_REQUEST['start'] / $max_msgs);
+
+				return;
+			}
+		}
+		createToken('admin-maint');
+		$context['page_title'] = $txt[$context['convert_to'] . '_title'];
+		$context['sub_template'] = 'convert_msgbody';
+
+		if (!empty($id_msg_exceeding))
+		{
+			if (count($id_msg_exceeding) > 100)
+			{
+				$query_msg = array_slice($id_msg_exceeding, 0, 100);
+				$context['exceeding_messages_morethan'] = sprintf($txt['exceeding_messages_morethan'], count($id_msg_exceeding));
+			}
+			else
+				$query_msg = $id_msg_exceeding;
+
+			$context['exceeding_messages'] = array();
+			$request = $smcFunc['db_query']('', '
+				SELECT id_msg, id_topic, subject
+				FROM {db_prefix}messages
+				WHERE id_msg IN ({array_int:messages})',
+				array(
+					'messages' => $query_msg,
+				)
+			);
+			while ($row = $smcFunc['db_fetch_assoc']($request))
+				$context['exceeding_messages'][] = '<a href="' . $scripturl . '?topic=' . $row['id_topic'] . '.msg' . $row['id_msg'] . '#msg' . $row['id_msg'] . '">' . $row['subject'] . '</a>';
+			$smcFunc['db_free_result']($request);
+		}
+	}
+}
+
 /**
  * Converts HTML-entities to their UTF-8 character equivalents.
  * This requires the admin_forum permission.

+ 5 - 26
Sources/ManagePosts.php

@@ -176,7 +176,7 @@ function SetCensor()
  */
 function ModifyPostSettings($return_config = false)
 {
-	global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_prefix;
+	global $context, $txt, $modSettings, $scripturl, $sourcedir, $smcFunc, $db_prefix, $db_type;
 
 	// All the settings...
 	$config_vars = array(
@@ -218,8 +218,8 @@ function ModifyPostSettings($return_config = false)
 	{
 		checkSession();
 
-		// If we're changing the message length let's check the column is big enough.
-		if (!empty($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'])
+		// If we're changing the message length (and we are using MySQL) let's check the column is big enough.
+		if (isset($_POST['max_messageLength']) && $_POST['max_messageLength'] != $modSettings['max_messageLength'] && $db_type == 'mysql')
 		{
 			db_extend('packages');
 
@@ -228,30 +228,9 @@ function ModifyPostSettings($return_config = false)
 				if ($column['name'] == 'body')
 					$body_type = $column['type'];
 
-			$indData = $smcFunc['db_list_indexes']('{db_prefix}messages', true);
-			foreach ($indData as $index)
-				foreach ($index['columns'] as $column)
-					if ($column == 'body' && $index['type'] == 'fulltext')
-						$fulltext = true;
-
 			if (isset($body_type) && ($_POST['max_messageLength'] > 65535 || $_POST['max_messageLength'] == 0) && $body_type == 'text')
-			{
-				// @todo Show an error message?!
-				// MySQL only likes fulltext indexes on text columns... for now?
-				if (!empty($fulltext))
-					$_POST['max_messageLength'] = 65535;
-				else
-				{
-					// Make it longer so we can do their limit.
-					$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'mediumtext'));
-				}
-			}
-			elseif (isset($body_type) && $_POST['max_messageLength'] <= 65535 && $body_type != 'text')
-			{
-				// @TODO shouldn't we warn that reducing the size of the column something could be lost?
-				// Shorten the column so we can have the benefit of fulltext searching again!
-				$smcFunc['db_change_column']('{db_prefix}messages', 'body', array('type' => 'text'));
-			}
+				fatal_lang_error('convert_to_mediumtext', false, array($scripturl . '?action=admin;area=maintain;sa=database'));
+
 		}
 		
 		// If we're changing the post preview length let's check its valid

+ 0 - 14
Sources/ManageSearch.php

@@ -235,20 +235,6 @@ function EditSearchMethod()
 				$context['fulltext_index'] = array_unique($context['fulltext_index']);
 		}
 
-		$request = $smcFunc['db_query']('', '
-			SHOW COLUMNS
-			FROM {db_prefix}messages',
-			array(
-			)
-		);
-		if ($request !== false)
-		{
-			while ($row = $smcFunc['db_fetch_assoc']($request))
-				if ($row['Field'] == 'body' && $row['Type'] == 'mediumtext')
-					$context['cannot_create_fulltext'] = true;
-			$smcFunc['db_free_result']($request);
-		}
-
 		if (preg_match('~^`(.+?)`\.(.+?)$~', $db_prefix, $match) !== 0)
 			$request = $smcFunc['db_query']('', '
 				SHOW TABLE STATUS

+ 67 - 0
Themes/default/ManageMaintenance.template.php

@@ -90,6 +90,29 @@ function template_maintain_database()
 			</div>
 		</div>';
 
+	// Show an option to convert the body column of the post table to MEDIUMTEXT or TEXT
+	if (isset($context['convert_to']))
+	{
+		echo '
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt[$context['convert_to'] . '_title'], '</h3>
+		</div>
+		<div class="windowbg">
+			<span class="topslice"><span></span></span>
+			<div class="content">
+				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertmsgbody" method="post" accept-charset="', $context['character_set'], '">
+					<p>', $txt['mediumtext_introduction'], '</p>',
+					$context['convert_to_suggest'] ? '<p class="infobox">' . $txt['convert_to_suggest_text'] . '</p>' : '', '
+					<hr class="hrcolor" />
+					<input type="submit" name="evaluate_conversion" value="', $txt['maintain_run_now'], '" class="button_submit" /><br class="clear_right" />
+					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
+				</form>
+			</div>
+			<span class="botslice"><span></span></span>
+		</div>';
+	}
+
 	// Show an option to convert to UTF-8 if we're not on UTF-8 yet.
 	if ($context['convert_utf8'])
 	{
@@ -662,4 +685,48 @@ function template_convert_entities()
 	<br class="clear" />';
 }
 
+function template_convert_msgbody()
+{
+	global $context, $txt, $settings, $scripturl;
+
+	echo '
+	<div id="manage_maintenance">
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt[$context['convert_to'] . '_title'], '</h3>
+		</div>
+		<div class="windowbg">
+			<span class="topslice"><span></span></span>
+			<div class="content">
+				<p>', $txt['body_checking_introduction'], '</p>';
+	if (!empty($context['exceeding_messages']))
+	{
+		echo '
+				<p class="noticebox">', $txt['exceeding_messages'], '
+				<ul>
+					<li>
+					', implode('</li><li>', $context['exceeding_messages']), '
+					</li>
+				</ul>';
+		if (!empty($context['exceeding_messages_morethan']))
+			echo '
+				<p>', $context['exceeding_messages_morethan'], '</p>';
+	}
+	else
+		echo '
+				<p class="infobox">', $txt['convert_to_text'], '</p>';
+
+	echo '
+				<form action="', $scripturl, '?action=admin;area=maintain;sa=database;activity=convertmsgbody" method="post" accept-charset="', $context['character_set'], '">
+				<hr class="hrcolor" />
+				<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+				<input type="hidden" name="', $context['admin-maint_token_var'], '" value="', $context['admin-maint_token'], '" />
+				<input type="submit" name="do_conversion" value="', $txt['entity_convert_proceed'], '" class="button_submit" />
+				<br class="clear_right" />
+				</form>
+			</div>
+			<span class="botslice"><span></span></span>
+		</div>
+	</div>
+	<br class="clear" />';
+}
 ?>

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

@@ -500,6 +500,7 @@ $txt['enableSpellChecking_warning'] = 'this does not work on all servers!';
 $txt['disable_wysiwyg'] = 'Disable WYSIWYG editor';
 $txt['max_messageLength'] = 'Maximum allowed post size';
 $txt['max_messageLength_zero'] = '0 for no max.';
+$txt['convert_to_mediumtext'] = 'Your database is not setup to accept messages longer than 65535 characters. Please use the <a href="%1$s">database maintenance</a> page to convert the database and then came back to increase the maximum allowed post size.';
 $txt['fixLongWords'] = 'Break up words with more letters than';
 $txt['fixLongWords_zero'] = '0 to disable.';
 $txt['fixLongWords_warning'] = 'this does not work on all servers!';

+ 9 - 0
Themes/default/languages/ManageMaintenance.english.php

@@ -187,6 +187,15 @@ $txt['utf8_utf8'] = 'UTF-8';
 $txt['utf8_db_version_too_low'] = 'The version of MySQL that your database server is using is not high enough to support UTF-8 properly. A minimum version of 4.1.2 is required.';
 $txt['utf8_cannot_convert_fulltext'] = 'Your messages table is using a fulltext index for use when searching.  You cannot proceed in converting to UTF-8 until that index is removed. You can re-create it after the conversion has been completed.';
 
+$txt['text_title'] = 'Convert to TEXT';
+$txt['mediumtext_title'] = 'Convert to MEDIUMTEXT';
+$txt['mediumtext_introduction'] = 'The default messages table can contain posts up to a size of 65535 characters, in order be able to store bigger texts the column must be converted to "MEDIUMTEXT". It is also possible to revert the column back to TEXT (that operation would reduce the space occupied), but <strong>only if</strong> none of the posts in your database exceed the size of 65535 characters. This condition will be verified before the conversion.';
+$txt['body_checking_introduction'] = 'This function will convert the column of your database that contains the text of the messages into a "TEXT" format (currently is "MEDIUMTEXT". This operation will allow to slightly reduce the amount of space occupied by each message (1 byte per message). If any message stored into the database is longer than 65535 characters it will be truncated and part of the text will be lost.';
+$txt['exceeding_messages'] = 'The following messages are longer than 65535 characters and will be truncated by the process:';
+$txt['exceeding_messages_morethan'] = 'And other %1$d';
+$txt['convert_to_text'] = 'No messages are longer than 65535 characters. You can safely proceed with the conversion without losing any part of the text.';
+$txt['convert_to_suggest_text'] = 'The messages body column in your database is currently set as MEDIUMTEXT, but the maximum allowed length set for the messages is lower than 65535 characters. You may free some space converting the column to TEXT.';
+
 $txt['entity_convert_title'] = 'Convert HTML-entities to UTF-8 characters';
 $txt['entity_convert_only_utf8'] = 'The database needs to be in UTF-8 format before HTML-entities can be converted to UTF-8';
 $txt['entity_convert_introduction'] = 'This function will convert all characters that are stored in the database as HTML-entities to UTF-8 characters. This is especially useful when you have just converted your forum from a character set like ISO-8859-1 while non-latin characters were used on the forum. The browser then sends all characters as HTML-entities. For example, the HTML-entity &amp;#945; represents the greek letter &#945; (alpha). Converting entities to UTF-8 will improve searching and sorting of text and reduce storage size.';