function ConvertEntities() { global $db_character_set, $modSettings, $context, $sourcedir, $smcFunc; isAllowedTo('admin_forum'); // Check to see if UTF-8 is currently the default character set. if ($modSettings['global_character_set'] !== 'UTF-8' || !isset($db_character_set) || $db_character_set !== 'utf8') { fatal_lang_error('entity_convert_only_utf8'); } // Some starting values. $context['table'] = empty($_REQUEST['table']) ? 0 : (int) $_REQUEST['table']; $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; $context['start_time'] = time(); $context['first_step'] = !isset($_REQUEST[$context['session_var']]); $context['last_step'] = false; // The first step is just a text screen with some explanation. if ($context['first_step']) { $context['sub_template'] = 'convert_entities'; return; } // Otherwise use the generic "not done" template. $context['sub_template'] = 'not_done'; $context['continue_post_data'] = ''; $context['continue_countdown'] = 3; // Now we're actually going to convert... checkSession('request'); // A list of tables ready for conversion. $tables = array('ban_groups', 'ban_items', 'boards', 'calendar', 'calendar_holidays', 'categories', 'log_errors', 'log_search_subjects', 'membergroups', 'members', 'message_icons', 'messages', 'package_servers', 'personal_messages', 'pm_recipients', 'polls', 'poll_choices', 'smileys', 'themes'); $context['num_tables'] = count($tables); // This function will do the conversion later on. $entity_replace = create_function('$string', ' $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));'); // Loop through all tables that need converting. for (; $context['table'] < $context['num_tables']; $context['table']++) { $cur_table = $tables[$context['table']]; $primary_key = ''; // Make sure we keep stuff unique! $primary_keys = array(); if (function_exists('apache_reset_timeout')) { @apache_reset_timeout(); } // Get a list of text columns. $columns = array(); $request = $smcFunc['db_query']('', ' SHOW FULL COLUMNS FROM {db_prefix}' . $cur_table, array()); while ($column_info = $smcFunc['db_fetch_assoc']($request)) { if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) { $columns[] = strtolower($column_info['Field']); } } // Get the column with the (first) primary key. $request = $smcFunc['db_query']('', ' SHOW KEYS FROM {db_prefix}' . $cur_table, array()); while ($row = $smcFunc['db_fetch_assoc']($request)) { if ($row['Key_name'] === 'PRIMARY') { if (empty($primary_key) || $row['Seq_in_index'] == 1 && !in_array(strtolower($row['Column_name']), $columns)) { $primary_key = $row['Column_name']; } $primary_keys[] = $row['Column_name']; } } $smcFunc['db_free_result']($request); // No primary key, no glory. // Same for columns. Just to be sure we've work to do! if (empty($primary_key) || empty($columns)) { continue; } // Get the maximum value for the primary key. $request = $smcFunc['db_query']('', ' SELECT MAX(' . $primary_key . ') FROM {db_prefix}' . $cur_table, array()); list($max_value) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); if (empty($max_value)) { continue; } while ($context['start'] <= $max_value) { // Retrieve a list of rows that has at least one entity to convert. $request = $smcFunc['db_query']('', ' SELECT {raw:primary_keys}, {raw:columns} FROM {db_prefix}{raw:cur_table} WHERE {raw:primary_key} BETWEEN {int:start} AND {int:start} + 499 AND {raw:like_compare} LIMIT 500', array('primary_keys' => implode(', ', $primary_keys), 'columns' => implode(', ', $columns), 'cur_table' => $cur_table, 'primary_key' => $primary_key, 'start' => $context['start'], 'like_compare' => '(' . implode(' LIKE \'%&#%\' OR ', $columns) . ' LIKE \'%&#%\')')); while ($row = $smcFunc['db_fetch_assoc']($request)) { $insertion_variables = array(); $changes = array(); foreach ($row as $column_name => $column_value) { if ($column_name !== $primary_key && strpos($column_value, '&#') !== false) { $changes[] = $column_name . ' = {string:changes_' . $column_name . '}'; $insertion_variables['changes_' . $column_name] = preg_replace_callback('~&#(\\d{1,7}|x[0-9a-fA-F]{1,6});~', 'fixchar__callback', $column_value); } } $where = array(); foreach ($primary_keys as $key) { $where[] = $key . ' = {string:where_' . $key . '}'; $insertion_variables['where_' . $key] = $row[$key]; } // Update the row. if (!empty($changes)) { $smcFunc['db_query']('', ' UPDATE {db_prefix}' . $cur_table . ' SET ' . implode(', ', $changes) . ' WHERE ' . implode(' AND ', $where), $insertion_variables); } } $smcFunc['db_free_result']($request); $context['start'] += 500; // After ten seconds interrupt. if (time() - $context['start_time'] > 10) { // Calculate an approximation of the percentage done. $context['continue_percent'] = round(100 * ($context['table'] + $context['start'] / $max_value) / $context['num_tables'], 1); $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;activity=convertentities;table=' . $context['table'] . ';start=' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']; return; } } $context['start'] = 0; } // Make sure all serialized strings are all right. require_once $sourcedir . '/Subs-Charset.php'; fix_serialized_columns(); // If we're here, we must be done. $context['continue_percent'] = 100; $context['continue_get_data'] = '?action=admin;area=maintain;sa=database;done=convertentities'; $context['last_step'] = true; $context['continue_countdown'] = -1; }
function ConvertEntities() { global $db_prefix, $db_character_set, $modSettings, $context, $sourcedir; isAllowedTo('admin_forum'); // Show the maintenance highlighted on the admin bar. adminIndex('maintain_forum'); // Check to see if UTF-8 is currently the default character set. if ($modSettings['global_character_set'] !== 'UTF-8' || !isset($db_character_set) || $db_character_set !== 'utf8') { fatal_lang_error('entity_convert_only_utf8'); } // Select the sub template from the Admin template. $context['sub_template'] = 'convert_entities'; // Some starting values. $context['table'] = empty($_REQUEST['table']) ? 0 : (int) $_REQUEST['table']; $context['start'] = empty($_REQUEST['start']) ? 0 : (int) $_REQUEST['start']; $context['start_time'] = time(); $context['first_step'] = !isset($_REQUEST['sesc']); $context['last_step'] = false; // The first step is just a text screen with some explanation. if ($context['first_step']) { return; } // Now we're actually going to convert... checkSession('get'); // A list of tables ready for conversion. $tables = array('ban_groups', 'ban_items', 'boards', 'calendar', 'calendar_holidays', 'categories', 'log_errors', 'log_search_subjects', 'membergroups', 'members', 'message_icons', 'messages', 'package_servers', 'personal_messages', 'pm_recipients', 'polls', 'poll_choices', 'smileys', 'themes'); $context['num_tables'] = count($tables); // This function will do the conversion later on. $entity_replace = create_function('$string', ' $num = substr($string, 0, 1) === \'x\' ? hexdec(substr($string, 1)) : (int) $string; return $num < 0x20 || $num > 0x10FFFF || ($num >= 0xD800 && $num <= 0xDFFF) ? \'\' : ($num < 0x80 ? \'&#\' . $num . \';\' : ($num < 0x800 ? chr(192 | $num >> 6) . chr(128 | $num & 63) : ($num < 0x10000 ? chr(224 | $num >> 12) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63) : chr(240 | $num >> 18) . chr(128 | $num >> 12 & 63) . chr(128 | $num >> 6 & 63) . chr(128 | $num & 63))));'); // Loop through all tables that need converting. for (; $context['table'] < $context['num_tables']; $context['table']++) { $cur_table = $tables[$context['table']]; $primary_key = ''; if (function_exists('apache_reset_timeout')) { apache_reset_timeout(); } // Get a list of text columns. $columns = array(); $request = db_query("\n\t\t\tSHOW FULL COLUMNS \n\t\t\tFROM {$db_prefix}{$cur_table}", __FILE__, __LINE__); while ($column_info = mysql_fetch_assoc($request)) { if (strpos($column_info['Type'], 'text') !== false || strpos($column_info['Type'], 'char') !== false) { $columns[] = $column_info['Field']; } } // Get the column with the (first) primary key. $request = db_query("\n\t\t\tSHOW KEYS\n\t\t\tFROM {$db_prefix}{$cur_table}", __FILE__, __LINE__); while ($row = mysql_fetch_assoc($request)) { if ($row['Key_name'] === 'PRIMARY' && $row['Seq_in_index'] == 1) { $primary_key = $row['Column_name']; break; } } mysql_free_result($request); // No primary key, no glory. if (empty($primary_key)) { continue; } // Get the maximum value for the primary key. $request = db_query("\n\t\t\tSELECT MAX({$primary_key})\n\t\t\tFROM {$db_prefix}{$cur_table}", __FILE__, __LINE__); list($max_value) = mysql_fetch_row($request); mysql_free_result($request); if (empty($max_value)) { continue; } while ($context['start'] <= $max_value) { // Retrieve a list of rows that has at least one entity to convert. $request = db_query("\n\t\t\t\tSELECT {$primary_key}, " . implode(', ', $columns) . "\n\t\t\t\tFROM {$db_prefix}{$cur_table}\n\t\t\t\tWHERE {$primary_key} BETWEEN {$context['start']} AND {$context['start']} + 499\n\t\t\t\t\tAND (" . implode(" LIKE '%&#%' OR ", $columns) . " LIKE '%&#%')\n\t\t\t\tLIMIT 500", __FILE__, __LINE__); while ($row = mysql_fetch_assoc($request)) { $changes = array(); foreach ($row as $column_name => $column_value) { if ($column_name !== $primary_key && strpos($column_value, '&#') !== false) { $changes[] = "{$column_name} = '" . addslashes(preg_replace('~(&#(\\d{1,7}|x[0-9a-fA-F]{1,6});)~e', '$entity_replace(\'\\2\')', $column_value)) . "'"; } } // Update the row. if (!empty($changes)) { db_query("\n\t\t\t\t\t\tUPDATE {$db_prefix}{$cur_table}\n\t\t\t\t\t\tSET \n\t\t\t\t\t\t\t" . implode(",\n\t\t\t\t\t\t\t", $changes) . "\n\t\t\t\t\t\tWHERE {$primary_key} = " . $row[$primary_key] . "\n\t\t\t\t\t\tLIMIT 1", __FILE__, __LINE__); } } mysql_free_result($request); $context['start'] += 500; // After ten seconds interrupt. if (time() - $context['start_time'] > 10) { // Calculate an approximation of the percentage done. $context['percent_done'] = round(100 * ($context['table'] + $context['start'] / $max_value) / $context['num_tables'], 1); $context['continue_get_data'] = '?action=convertentities;table=' . $context['table'] . ';start=' . $context['start'] . ';sesc=' . $context['session_id']; return; } } $context['start'] = 0; } // Make sure all serialized strings are all right. require_once $sourcedir . '/Subs-Charset.php'; fix_serialized_columns(); // If we're here, we must be done. $context['percent_done'] = 100; $context['continue_get_data'] = '?action=maintain'; $context['last_step'] = true; }