- added confirm box to ucp zebra (adding fried/foe)
[phpbb.git] / phpBB / install / install_convert.php
blob83988ff05fc9ac84033863baf04c2eaceb44b4b7
1 <?php
2 /**
4 * @package install
5 * @version $Id$
6 * @copyright (c) 2006 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
14 if (!defined('IN_INSTALL'))
16 // Someone has tried to access the file direct. This is not a good idea, so exit
17 exit;
20 if (!empty($setmodules))
22 $module[] = array(
23 'module_type' => 'install',
24 'module_title' => 'CONVERT',
25 'module_filename' => substr(basename(__FILE__), 0, -strlen($phpEx)-1),
26 'module_order' => 20,
27 'module_subs' => '',
28 'module_stages' => array('INTRO', 'SETTINGS', 'IN_PROGRESS', 'FINAL'),
29 'module_reqs' => ''
33 class convert
35 var $options = array();
37 var $convertor_tag = '';
38 var $src_table_prefix = '';
40 var $convertor_data = array();
41 var $tables = array();
42 var $config_schema = array();
43 var $convertor = array();
44 var $truncate_statement = 'DELETE FROM ';
46 var $fulltext_search;
48 // Batch size, can be adjusted by the conversion file
49 // For big boards a value of 6000 seems to be optimal
50 var $batch_size = 2000;
51 // Number of rows to be inserted at once (extended insert) if supported
52 // For installations having enough memory a value of 60 may be good.
53 var $num_wait_rows = 20;
55 // Mysqls internal recoding engine messing up with our (better) functions? We at least support more encodings than mysql so should use it in favor.
56 var $mysql_convert = false;
58 var $p_master;
60 function convert(&$p_master)
62 $this->p_master = &$p_master;
66 class install_convert extends module
68 /**
69 * Variables used while converting, they are accessible from the global variable $convert
71 function install_convert(&$p_master)
73 $this->p_master = &$p_master;
76 function main($mode, $sub)
78 global $lang, $template, $phpbb_root_path, $phpEx, $cache, $config;
79 global $convert;
81 $this->tpl_name = 'install_convert';
82 $this->mode = $mode;
84 $convert = new convert($this->p_master);
86 switch ($sub)
88 case 'intro':
89 // Try opening config file
90 // @todo If phpBB is not installed, we need to do a cut-down installation here
91 // For now, we redirect to the installation script instead
92 if (@file_exists($phpbb_root_path . 'config.' . $phpEx))
94 include($phpbb_root_path . 'config.' . $phpEx);
97 if (!defined('PHPBB_INSTALLED'))
99 $template->assign_vars(array(
100 'S_NOT_INSTALLED' => true,
101 'TITLE' => $lang['BOARD_NOT_INSTALLED'],
102 'BODY' => sprintf($lang['BOARD_NOT_INSTALLED_EXPLAIN'], append_sid($phpbb_root_path . 'install/index.' . $phpEx, 'mode=install')),
105 return;
108 require($phpbb_root_path . 'config.' . $phpEx);
109 require($phpbb_root_path . 'includes/constants.' . $phpEx);
110 require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx);
111 require($phpbb_root_path . 'includes/functions_convert.' . $phpEx);
113 $db = new $sql_db();
114 $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false);
115 unset($dbpasswd);
117 // We need to fill the config to let internal functions correctly work
118 $sql = 'SELECT *
119 FROM ' . CONFIG_TABLE;
120 $result = $db->sql_query($sql);
122 $config = array();
123 while ($row = $db->sql_fetchrow($result))
125 $config[$row['config_name']] = $row['config_value'];
127 $db->sql_freeresult($result);
130 // Detect if there is already a conversion in progress at this point and offer to resume
131 // It's quite possible that the user will get disconnected during a large conversion so they need to be able to resume it
132 $new_conversion = request_var('new_conv', 0);
134 if ($new_conversion)
136 $config['convert_progress'] = '';
137 $db->sql_query('DELETE FROM ' . CONFIG_TABLE . " WHERE config_name = 'convert_progress'");
140 // Let's see if there is a conversion in the works...
141 $options = array();
142 if (!empty($config['convert_progress']) && !empty($config['convert_options']))
144 $options = unserialize($config['convert_progress']);
145 $options = array_merge($options, unserialize($config['convert_options']));
148 // This information should have already been checked once, but do it again for safety
149 if (!empty($options) && !empty($options['tag']) && isset($options['table_prefix']))
151 $this->page_title = $lang['CONTINUE_CONVERT'];
153 $template->assign_vars(array(
154 'TITLE' => $lang['CONTINUE_CONVERT'],
155 'BODY' => $lang['CONTINUE_CONVERT_BODY'],
156 'L_NEW' => $lang['CONVERT_NEW_CONVERSION'],
157 'L_CONTINUE' => $lang['CONTINUE_OLD_CONVERSION'],
158 'S_CONTINUE' => true,
160 'U_NEW_ACTION' => $this->p_master->module_url . "?mode=$mode&amp;sub=intro&amp;new_conv=1",
161 'U_CONTINUE_ACTION' => $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag={$options['tag']}{$options['step']}",
164 return;
167 $this->list_convertors($mode, $sub);
169 break;
171 case 'settings':
172 $this->get_convert_settings($mode, $sub);
173 break;
175 case 'in_progress':
176 $this->convert_data($mode, $sub);
177 break;
179 case 'final':
180 $this->page_title = $lang['CONVERT_COMPLETE'];
182 $template->assign_vars(array(
183 'TITLE' => $lang['CONVERT_COMPLETE'],
184 'BODY' => $lang['CONVERT_COMPLETE_EXPLAIN'],
187 break;
192 * Generate a list of all available conversion modules
194 function list_convertors($mode, $sub)
196 global $lang, $template, $phpbb_root_path, $phpEx;
198 $this->page_title = $lang['SUB_INTRO'];
200 $template->assign_vars(array(
201 'TITLE' => $lang['CONVERT_INTRO'],
202 'BODY' => $lang['CONVERT_INTRO_BODY'],
204 'L_AUTHOR' => $lang['AUTHOR'],
205 'L_AVAILABLE_CONVERTORS' => $lang['AVAILABLE_CONVERTORS'],
206 'L_CONVERT' => $lang['CONVERT'],
207 'L_NO_CONVERTORS' => $lang['NO_CONVERTORS'],
208 'L_OPTIONS' => $lang['OPTIONS'],
209 'L_SOFTWARE' => $lang['SOFTWARE'],
210 'L_VERSION' => $lang['VERSION'],
212 'S_LIST' => true,
215 $convertors = $sort = array();
216 $get_info = true;
218 $handle = @opendir('./convertors/');
220 if (!$handle)
222 $this->error('Unable to access the convertors directory', __LINE__, __FILE__);
225 while ($entry = readdir($handle))
227 if (preg_match('/^convert_([a-z0-9_]+).' . $phpEx . '/i', $entry, $m))
229 include('./convertors/' . $entry);
230 if (isset($convertor_data))
232 $sort[strtolower($convertor_data['forum_name'])] = sizeof($convertors);
234 $convertors[] = array(
235 'tag' => $m[1],
236 'forum_name' => $convertor_data['forum_name'],
237 'version' => $convertor_data['version'],
238 'table_prefix' => $convertor_data['table_prefix'],
239 'author' => $convertor_data['author']
242 unset($convertor_data);
245 closedir($handle);
247 @ksort($sort);
249 foreach ($sort as $void => $index)
251 $template->assign_block_vars('convertors', array(
252 'AUTHOR' => $convertors[$index]['author'],
253 'SOFTWARE' => $convertors[$index]['forum_name'],
254 'VERSION' => $convertors[$index]['version'],
256 'U_CONVERT' => $this->p_master->module_url . "?mode=$mode&amp;sub=settings&amp;tag=" . $convertors[$index]['tag'],
263 function get_convert_settings($mode, $sub)
265 global $lang, $template, $db, $phpbb_root_path, $phpEx, $config, $cache;
267 require($phpbb_root_path . 'config.' . $phpEx);
268 require($phpbb_root_path . 'includes/constants.' . $phpEx);
269 require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx);
270 require($phpbb_root_path . 'includes/functions_convert.' . $phpEx);
272 $db = new $sql_db();
273 $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false);
274 unset($dbpasswd);
276 $this->page_title = $lang['STAGE_SETTINGS'];
278 // We need to fill the config to let internal functions correctly work
279 $sql = 'SELECT *
280 FROM ' . CONFIG_TABLE;
281 $result = $db->sql_query($sql);
283 $config = array();
284 while ($row = $db->sql_fetchrow($result))
286 $config[$row['config_name']] = $row['config_value'];
288 $db->sql_freeresult($result);
290 $convertor_tag = request_var('tag', '');
292 if (empty($convertor_tag))
294 $this->p_master->error($lang['NO_CONVERT_SPECIFIED'], __LINE__, __FILE__);
296 $get_info = true;
298 // check security implications of direct inclusion
299 $convertor_tag = basename($convertor_tag);
300 if (!file_exists('./convertors/convert_' . $convertor_tag . '.' . $phpEx))
302 $this->p_master->error($lang['CONVERT_NOT_EXIST'], __LINE__, __FILE__);
305 include('./convertors/convert_' . $convertor_tag . '.' . $phpEx);
307 // The test_file is a file that should be present in the location of the old board.
308 if (!isset($test_file))
310 $this->p_master->error($lang['DEV_NO_TEST_FILE'], __LINE__, __FILE__);
313 $submit = (isset($_POST['submit'])) ? true : false;
315 $src_table_prefix = request_var('src_table_prefix', $convertor_data['table_prefix']);
316 $forum_path = request_var('forum_path', $convertor_data['forum_path']);
317 $refresh = request_var('refresh', 1);
319 // Default URL of the old board
320 // @todo Are we going to use this for attempting to convert URL references in posts, or should we remove it?
321 // -> We should convert old urls to the new relative urls format
322 // $src_url = request_var('src_url', 'Not in use at the moment');
324 $error = array();
325 if ($submit)
327 if (!file_exists('./../' . $forum_path . '/' . $test_file))
329 $error[] = sprintf($lang['COULD_NOT_FIND_PATH'], $forum_path);
332 // The forum prefix of the old and the new forum can't be the same because the
333 // convertor requires all tables to be in one database. I.e. there can't be
334 // two tables named 'phpbb_users'
335 if ($src_table_prefix == $table_prefix)
337 $error[] = sprintf($lang['TABLE_PREFIX_SAME'], $src_table_prefix);
340 // Check table prefix
341 if (!sizeof($error))
343 $db->sql_return_on_error(true);
345 // Try to select one row from the first table to see if the prefix is OK
346 $result = $db->sql_query_limit('SELECT * FROM ' . $src_table_prefix . $tables[0], 1);
348 if (!$result)
350 $prefixes = array();
351 if ($result = $db->sql_query('SHOW TABLES'))
353 while ($row = $db->sql_fetchrow($result))
355 if (sizeof($row) > 1)
357 compare_table($tables, $row[0], $prefixes);
359 else if (list(, $tablename) = @each($row))
361 compare_table($tables, $tablename, $prefixes);
364 $db->sql_freeresult($result);
367 foreach ($prefixes as $prefix => $count)
369 if ($count >= sizeof($tables))
371 $possible_prefix = $prefix;
372 break;
376 $msg = '';
377 if (!empty($convertor_data['table_prefix']))
379 $msg .= sprintf($lang['DEFAULT_PREFIX_IS'], $convertor_data['forum_name'], $convertor_data['table_prefix']);
382 if (!empty($possible_prefix))
384 $msg .= '<br />';
385 $msg .= ($possible_prefix == '*') ? $lang['BLANK_PREFIX_FOUND'] : sprintf($lang['PREFIX_FOUND'], $possible_prefix);
386 $src_table_prefix = ($possible_prefix == '*') ? '' : $possible_prefix;
389 $error[] = $msg;
391 $db->sql_freeresult($result);
392 $db->sql_return_on_error(false);
395 if (!sizeof($error))
397 // Save convertor Status
398 set_config('convert_progress', serialize(array('step' => '', 'table_prefix' => $src_table_prefix, 'tag' => $convertor_tag)), true);
400 // Save options
401 set_config('convert_options', serialize(array('forum_path' => './../' . $forum_path, 'refresh' => $refresh)), true);
403 $template->assign_block_vars('checks', array(
404 'TITLE' => $lang['SPECIFY_OPTIONS'],
405 'RESULT' => $lang['CONVERT_SETTINGS_VERIFIED'],
408 $template->assign_vars(array(
409 'L_SUBMIT' => $lang['BEGIN_CONVERT'],
410 // 'S_HIDDEN' => $s_hidden_fields,
411 'U_ACTION' => $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag=$convertor_tag",
414 return;
416 else
418 $template->assign_block_vars('checks', array(
419 'TITLE' => $lang['SPECIFY_OPTIONS'],
420 'RESULT' => '<b style="color:red">' . implode('<br />', $error) . '</b>',
423 } // end submit
425 foreach ($this->convert_options as $config_key => $vars)
427 if (!is_array($vars) && strpos($config_key, 'legend') === false)
429 continue;
432 if (strpos($config_key, 'legend') !== false)
434 $template->assign_block_vars('options', array(
435 'S_LEGEND' => true,
436 'LEGEND' => $lang[$vars])
439 continue;
442 $options = isset($vars['options']) ? $vars['options'] : '';
444 $template->assign_block_vars('options', array(
445 'KEY' => $config_key,
446 'TITLE' => $lang[$vars['lang']],
447 'S_EXPLAIN' => $vars['explain'],
448 'S_LEGEND' => false,
449 'TITLE_EXPLAIN' => ($vars['explain']) ? $lang[$vars['lang'] . '_EXPLAIN'] : '',
450 'CONTENT' => $this->p_master->input_field($config_key, $vars['type'], $$config_key, $options),
455 $template->assign_vars(array(
456 'L_SUBMIT' => $lang['BEGIN_CONVERT'],
457 'U_ACTION' => $this->p_master->module_url . "?mode=$mode&amp;sub=settings&amp;tag=$convertor_tag",
462 * The function which does the actual work (or dispatches it to the relevant places)
464 function convert_data($mode, $sub)
466 global $template, $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache;
467 global $convert, $convert_row, $message_parser, $skip_rows;
469 require($phpbb_root_path . 'config.' . $phpEx);
470 require($phpbb_root_path . 'includes/constants.' . $phpEx);
471 require($phpbb_root_path . 'includes/db/' . $dbms . '.' . $phpEx);
472 require($phpbb_root_path . 'includes/functions_convert.' . $phpEx);
474 $db = new $sql_db();
475 $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false);
476 unset($dbpasswd);
478 $sql = 'SELECT *
479 FROM ' . CONFIG_TABLE;
480 $result = $db->sql_query($sql);
482 $config = array();
483 while ($row = $db->sql_fetchrow($result))
485 $config[$row['config_name']] = $row['config_value'];
487 $db->sql_freeresult($result);
489 // Override a couple of config variables for the duration
490 $config['max_quote_depth'] = 0;
492 // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues
493 $config['max_post_chars'] = -1;
495 $convert->mysql_convert = false;
497 switch ($db->sql_layer)
499 // Thanks MySQL, for silently converting...
500 case 'mysql':
501 case 'mysql4':
502 if (version_compare($db->mysql_version, '4.1.3', '>='))
504 $convert->mysql_convert = true;
506 break;
508 case 'mysqli':
509 $convert->mysql_convert = true;
510 break;
513 // Set up a user as well. We _should_ have enough of a database here at this point to do this
514 // and it helps for any core code we call
515 $user->session_begin();
516 $user->page = $user->extract_current_page($phpbb_root_path);
518 // This is a little bit of a fudge, but it allows the language entries to be available to the
519 // core code without us loading them again
520 $user->lang = &$lang;
522 $this->page_title = $user->lang['STAGE_IN_PROGRESS'];
524 $convert->options = array();
525 if (isset($config['convert_progress']))
527 $convert->options = unserialize($config['convert_progress']);
528 $convert->options = array_merge($convert->options, unserialize($config['convert_options']));
531 // This information should have already been checked once, but do it again for safety
532 if (empty($convert->options) || empty($convert->options['tag']) || !isset($convert->options['table_prefix']))
534 $this->p_master->error($user->lang['NO_CONVERT_SPECIFIED'], __LINE__, __FILE__);
537 // Make some short variables accessible, for easier referencing
538 $convert->convertor_tag = basename($convert->options['tag']);
539 $convert->src_table_prefix = $convert->options['table_prefix'];
540 $convert->truncate_statement = ($db->sql_layer != 'sqlite') ? 'TRUNCATE TABLE ' : 'DELETE FROM ';
542 $get_info = false;
544 // check security implications of direct inclusion
545 if (!file_exists('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx))
547 $this->p_master->error($user->lang['CONVERT_NOT_EXIST'], __LINE__, __FILE__);
550 if (file_exists('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx))
552 include('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx);
555 $get_info = true;
556 include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx);
558 // Map some variables...
559 $convert->convertor_data = $convertor_data;
560 $convert->tables = $tables;
561 $convert->config_schema = $config_schema;
563 // Now include the real data
564 $get_info = false;
565 include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx);
567 $convert->convertor_data = $convertor_data;
568 $convert->tables = $tables;
569 $convert->config_schema = $config_schema;
570 $convert->convertor = $convertor;
572 // The test_file is a file that should be present in the location of the old board.
573 if (!file_exists($convert->options['forum_path'] . '/' . $test_file))
575 $this->p_master->error(sprintf($user->lang['COULD_NOT_FIND_PATH'], $convert->options['forum_path']), __LINE__, __FILE__);
578 $search_type = $config['search_type'];
580 if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx))
582 trigger_error('NO_SUCH_SEARCH_MODULE');
585 require($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx);
587 $error = false;
588 $convert->fulltext_search = new $search_type($error);
590 if ($error)
592 trigger_error($error);
595 include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
596 $message_parser = new parse_message();
598 $jump = request_var('jump', 0);
599 $sync_batch = request_var('sync_batch', -1);
600 $last_statement = request_var('last', 0);
602 // We are running sync...
603 if ($sync_batch >= 0)
605 $this->sync_forums($sync_batch);
606 return;
609 if ($jump)
611 $this->jump($jump, $last_statement);
612 return;
615 $current_table = request_var('current_table', 0);
616 $old_current_table = min(-1, $current_table - 1);
617 $skip_rows = request_var('skip_rows', 0);
619 if (!$current_table && !$skip_rows)
621 if (empty($_REQUEST['confirm']))
623 // If avatars / ranks / smilies folders are specified make sure they are writable
624 $bad_folders = array();
626 $local_paths = array(
627 'avatar_path' => path($config['avatar_path']),
628 'avatar_gallery_path' => path($config['avatar_gallery_path']),
629 'icons_path' => path($config['icons_path']),
630 'ranks_path' => path($config['ranks_path']),
631 'smilies_path' => path($config['smilies_path'])
634 foreach ($local_paths as $folder => $local_path)
636 if (isset($convert->convertor[$folder]))
638 if (empty($convert->convertor['test_file']))
640 // test_file is mandantory at the moment so this should never be reached, but just in case...
641 $this->p_master->error($user->lang['DEV_NO_TEST_FILE'], __LINE__, __FILE__);
644 if (!$local_path || !is_writeable($phpbb_root_path . $local_path))
646 if (!$local_path)
648 $bad_folders[] = sprintf($user->lang['CONFIG_PHPBB_EMPTY'], $folder);
650 else
652 $bad_folders[] = $local_path;
658 if (sizeof($bad_folders))
660 $msg = (sizeof($bad_folders) == 1) ? $user->lang['MAKE_FOLDER_WRITABLE'] : $user->lang['MAKE_FOLDERS_WRITABLE'];
661 sort($bad_folders);
662 $this->p_master->error(sprintf($msg, implode('<br />', $bad_folders)), __LINE__, __FILE__, true);
664 $template->assign_vars(array(
665 'L_SUBMIT' => $user->lang['INSTALL_TEST'],
666 'U_ACTION' => $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag={$convert->convertor_tag}",
668 return;
671 // Grab all the tables used in convertor
672 $missing_tables = $tables_list = $aliases = array();
674 foreach ($convert->convertor['schema'] as $schema)
676 // Skip those not used (because of addons/plugins not detected)
677 if (!$schema['target'])
679 continue;
682 foreach ($schema as $key => $val)
684 // we're dealing with an array like:
685 // array('forum_status', 'forums.forum_status', 'is_item_locked')
686 if (is_int($key) && !empty($val[1]))
688 $temp_data = $val[1];
689 if (!is_array($temp_data))
691 $temp_data = array($temp_data);
694 foreach ($temp_data as $val)
696 if (preg_match('/([a-z0-9_]+)\.([a-z0-9_]+)\)* ?A?S? ?([a-z0-9_]*?)\.?([a-z0-9_]*)$/i', $val, $m))
698 $table = $convert->src_table_prefix . $m[1];
699 $tables_list[$table] = $table;
701 if (!empty($m[3]))
703 $aliases[] = $convert->src_table_prefix . $m[3];
708 // 'left_join' => 'topics LEFT JOIN vote_desc ON topics.topic_id = vote_desc.topic_id AND topics.topic_vote = 1'
709 else if ($key == 'left_join')
711 // Convert the value if it wasn't an array already.
712 if (!is_array($val))
714 $val = array($val);
717 for ($j = 0; $j < sizeof($val); ++$j)
719 if (preg_match('/LEFT JOIN ([a-z0-9_]+) AS ([a-z0-9_]+)/i', $val[$j], $m))
721 $table = $convert->src_table_prefix . $m[1];
722 $tables_list[$table] = $table;
724 if (!empty($m[2]))
726 $aliases[] = $convert->src_table_prefix . $m[2];
734 // Remove aliased tables from $tables_list
735 foreach ($aliases as $alias)
737 unset($tables_list[$alias]);
740 // Check if the tables that we need exist
741 $db->sql_return_on_error(true);
742 foreach ($tables_list as $table => $null)
744 $sql = 'SELECT 1 FROM ' . $table;
745 $_result = $db->sql_query_limit($sql, 1);
747 if (!$_result)
749 $missing_tables[] = $table;
751 $db->sql_freeresult($_result);
753 $db->sql_return_on_error(false);
755 // Throw an error if some tables are missing
756 // We used to do some guessing here, but since we have a suggestion of possible values earlier, I don't see it adding anything here to do it again
758 if (sizeof($missing_tables) == sizeof($tables_list))
760 $this->p_master->error($user->lang['NO_TABLES_FOUND'] . ' ' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
762 else if (sizeof($missing_tables))
764 $this->p_master->error(sprintf($user->lang['TABLES_MISSING'], implode(', ', $missing_tables)) . '<br /><br />' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__);
767 $step = '&amp;confirm=1';
768 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
770 $msg = $user->lang['PRE_CONVERT_COMPLETE'] . '</p><p>' . sprintf($user->lang['AUTHOR_NOTES'], $convert->convertor_data['author_notes']);
771 $url = $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
773 $template->assign_vars(array(
774 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
775 'L_MESSAGE' => $msg,
776 'U_ACTION' => $url,
779 return;
780 } // if (empty($_REQUEST['confirm']))
782 $template->assign_block_vars('checks', array(
783 'S_LEGEND' => true,
784 'LEGEND' => $user->lang['STARTING_CONVERT'],
787 // Convert the config table and load the settings of the old board
788 if (!empty($convert->config_schema))
790 restore_config($convert->config_schema);
793 $template->assign_block_vars('checks', array(
794 'TITLE' => $user->lang['CONFIG_CONVERT'],
795 'RESULT' => $user->lang['DONE'],
798 // Now process queries and execute functions that have to be executed prior to the conversion
799 if (!empty($convert->convertor['execute_first']))
801 eval($convert->convertor['execute_first']);
804 if (!empty($convert->convertor['query_first']))
806 if (!is_array($convert->convertor['query_first']))
808 $convert->convertor['query_first'] = array($convert->convertor['query_first']);
811 foreach ($convert->convertor['query_first'] as $query_first)
813 $db->sql_query($query_first);
817 $template->assign_block_vars('checks', array(
818 'TITLE' => $user->lang['PREPROCESS_STEP'],
819 'RESULT' => $user->lang['DONE'],
821 } // if (!$current_table && !$skip_rows)
823 $template->assign_block_vars('checks', array(
824 'S_LEGEND' => true,
825 'LEGEND' => $user->lang['FILLING_TABLES'],
828 // This loop takes one target table and processes it
829 while ($current_table < sizeof($convert->convertor['schema']))
831 $schema = $convert->convertor['schema'][$current_table];
833 // The target table isn't set, this can be because a module (for example the attachement mod) is taking care of this.
834 if (empty($schema['target']))
836 $current_table++;
837 continue;
840 $template->assign_block_vars('checks', array(
841 'TITLE' => sprintf($user->lang['FILLING_TABLE'], $schema['target']),
844 // This is only the case when we first start working on the tables.
845 if (!$skip_rows)
847 // process execute_first and query_first for this table...
848 if (!empty($schema['execute_first']))
850 eval($schema['execute_first']);
853 if (!empty($schema['query_first']))
855 if (!is_array($schema['query_first']))
857 $schema['query_first'] = array($schema['query_first']);
860 foreach ($schema['query_first'] as $query_first)
862 $db->sql_query($query_first);
867 // Process execute_always for this table
868 // This is for code which needs to be executed on every pass of this table if
869 // it gets split because of time restrictions
870 if (!empty($schema['execute_always']))
872 eval($schema['execute_always']);
876 // Set up some variables
878 // $waiting_rows holds rows for multirows insertion (MySQL only)
879 // $src_tables holds unique tables with aliases to select from
880 // $src_fields will quickly refer source fields (or aliases) corresponding to the current index
881 // $select_fields holds the names of the fields to retrieve
884 $sql_data = array(
885 'source_fields' => array(),
886 'target_fields' => array(),
887 'source_tables' => array(),
888 'select_fields' => array(),
891 // This statement is building the keys for later insertion.
892 $insert_query = $this->build_insert_query($schema, $sql_data, $current_table);
894 // If no source table is affected, we skip the table
895 if (empty($sql_data['source_tables']))
897 $skip_rows = 0;
898 $current_table++;
899 continue;
902 $distinct = (!empty($schema['distinct'])) ? 'DISTINCT ' : '';
904 $sql = 'SELECT ' . $distinct . implode(', ', $sql_data['select_fields']) . " \nFROM " . implode(', ', $sql_data['source_tables']);
906 // Where
907 $sql .= (!empty($schema['where'])) ? "\nWHERE (" . $schema['where'] . ')' : '';
909 // Group By
910 $sql .= (!empty($schema['group_by'])) ? "\nGROUP BY " . $schema['group_by'] : '';
912 // Having
913 $sql .= (!empty($schema['having'])) ? "\nHAVING " . $schema['having'] : '';
915 // Order By
916 $sql .= (!empty($schema['order_by'])) ? "\nORDER BY " . $schema['order_by'] : '';
918 // Counting basically holds the amount of rows processed.
919 $counting = -1;
920 $batch_time = 0;
922 while (($counting === -1 || $counting >= $convert->batch_size) && still_on_time())
924 $old_current_table = $current_table;
926 $rows = '';
927 $waiting_rows = array();
929 if (!empty($batch_time))
931 $mtime = explode(' ', microtime());
932 $mtime = $mtime[0] + $mtime[1];
933 $rows = ceil($counting/($mtime - $batch_time)) . " rows/s ($counting rows) | ";
936 $template->assign_block_vars('checks', array(
937 'TITLE' => "skip_rows = $skip_rows",
938 'RESULT' => $rows . ((defined('DEBUG_EXTRA') && function_exists('memory_get_usage')) ? ceil(memory_get_usage()/1024) . ' KB' : ''),
941 $mtime = explode(' ', microtime());
942 $batch_time = $mtime[0] + $mtime[1];
944 if ($convert->mysql_convert)
946 $db->sql_query("SET NAMES 'binary'");
949 // Take skip rows into account and only fetch batch_size amount of rows
950 $___result = $db->sql_query_limit($sql, $convert->batch_size, $skip_rows);
952 if ($convert->mysql_convert)
954 $db->sql_query("SET NAMES 'utf8'");
957 // This loop processes each row
958 $counting = 0;
960 $convert->row = $convert_row = array();
962 // Now handle the rows until time is over or no more rows to process...
963 while (still_on_time())
965 $convert_row = $db->sql_fetchrow($___result);
967 if (!$convert_row)
969 // move to the next batch or table
970 $db->sql_freeresult($___result);
971 break;
974 // With this we are able to always save the last state
975 $convert->row = $convert_row;
977 // Increment the counting variable, it stores the number of rows we have processed
978 $counting++;
980 $insert_values = array();
982 $sql_flag = $this->process_row($schema, $sql_data, $insert_values);
984 if ($sql_flag === true)
986 switch ($db->sql_layer)
988 // If MySQL, we'll wait to have num_wait_rows rows to submit at once
989 case 'mysql':
990 case 'mysql4':
991 case 'mysqli':
992 $waiting_rows[] = '(' . implode(', ', $insert_values) . ')';
994 if (sizeof($waiting_rows) >= $convert->num_wait_rows)
996 $errored = false;
998 $db->sql_return_on_error(true);
1000 if (!$db->sql_query($insert_query . implode(', ', $waiting_rows)))
1002 $errored = true;
1004 $db->sql_return_on_error(false);
1006 if ($errored)
1008 $db->sql_return_on_error(true);
1010 // Because it errored out we will try to insert the rows one by one... most of the time this
1011 // is caused by duplicate entries - but we also do not want to miss one...
1012 foreach ($waiting_rows as $waiting_sql)
1014 if (!$db->sql_query($insert_query . $waiting_sql))
1016 $this->p_master->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
1020 $db->sql_return_on_error(false);
1023 $waiting_rows = array();
1026 break;
1028 default:
1029 $insert_sql = $insert_query . '(' . implode(', ', $insert_values) . ')';
1031 $db->sql_return_on_error(true);
1033 if (!$db->sql_query($insert_sql))
1035 $this->p_master->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
1037 $db->sql_return_on_error(false);
1039 $waiting_rows = array();
1041 break;
1045 $skip_rows++;
1047 $db->sql_freeresult($___result);
1049 // We might still have some rows waiting
1050 if (sizeof($waiting_rows))
1052 $errored = false;
1053 $db->sql_return_on_error(true);
1055 if (!$db->sql_query($insert_query . implode(', ', $waiting_rows)))
1057 $errored = true;
1059 $db->sql_return_on_error(false);
1061 if ($errored)
1063 $db->sql_return_on_error(true);
1065 // Because it errored out we will try to insert the rows one by one... most of the time this
1066 // is caused by duplicate entries - but we also do not want to miss one...
1067 foreach ($waiting_rows as $waiting_sql)
1069 $db->sql_query($insert_query . $waiting_sql);
1070 $this->p_master->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true);
1073 $db->sql_return_on_error(false);
1076 $waiting_rows = array();
1080 // When we reach this point, either the current table has been processed or we're running out of time.
1081 if (still_on_time() && $counting < $convert->batch_size/* && !defined('DEBUG_EXTRA')*/)
1083 $skip_rows = 0;
1084 $current_table++;
1086 else
1088 if (still_on_time() && $counting < $convert->batch_size)
1090 $skip_rows = 0;
1091 $current_table++;
1094 // Looks like we ran out of time.
1095 $step = '&amp;current_table=' . $current_table . '&amp;skip_rows=' . $skip_rows;
1097 // Save convertor Status
1098 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1100 $current_table++;
1101 // $percentage = ($skip_rows == 0) ? 0 : floor(100 / ($total_rows / $skip_rows));
1103 $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $current_table, sizeof($convert->convertor['schema']));
1105 $url = $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1107 $template->assign_vars(array(
1108 'L_MESSAGE' => $msg,
1109 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
1110 'U_ACTION' => $url,
1113 $this->meta_refresh($url);
1114 return;
1118 // Process execute_last then we'll be done
1119 $step = '&amp;jump=1';
1121 // Save convertor Status
1122 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1124 $url = $this->p_master->module_url . "?mode=$mode&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1126 $template->assign_vars(array(
1127 'L_SUBMIT' => $user->lang['FINAL_STEP'],
1128 'U_ACTION' => $url,
1131 $this->meta_refresh($url);
1132 return;
1136 * Sync function being executed at the very end...
1138 function sync_forums($sync_batch)
1140 global $template, $user, $db, $phpbb_root_path, $phpEx, $config, $cache;
1141 global $convert;
1143 $template->assign_block_vars('checks', array(
1144 'S_LEGEND' => true,
1145 'LEGEND' => $user->lang['SYNC_TOPICS'],
1148 $batch_size = $convert->batch_size;
1150 $sql = 'SELECT MIN(topic_id) as min_value, MAX(topic_id) AS max_value
1151 FROM ' . TOPICS_TABLE;
1152 $result = $db->sql_query($sql);
1153 $row = $db->sql_fetchrow($result);
1154 $db->sql_freeresult($result);
1156 // Set values of minimum/maximum primary value for this table.
1157 $primary_min = $row['min_value'];
1158 $primary_max = $row['max_value'];
1160 if ($sync_batch == 0)
1162 $sync_batch = (int) $primary_min;
1165 if ($sync_batch == 0)
1167 $sync_batch = 1;
1170 // Fetch a batch of rows, process and insert them.
1171 while ($sync_batch <= $primary_max && still_on_time())
1173 $end = ($sync_batch + $batch_size - 1);
1175 // Sync all topics in batch mode...
1176 sync('topic_approved', 'range', 'topic_id BETWEEN ' . $sync_batch . ' AND ' . $end, true, false);
1177 sync('topic', 'range', 'topic_id BETWEEN ' . $sync_batch . ' AND ' . $end, true, true);
1179 $template->assign_block_vars('checks', array(
1180 'TITLE' => sprintf($user->lang['SYNC_TOPIC_ID'], $sync_batch, ($sync_batch + $batch_size)) . ((defined('DEBUG_EXTRA') && function_exists('memory_get_usage')) ? ' [' . ceil(memory_get_usage()/1024) . ' KB]' : ''),
1181 'RESULT' => $user->lang['DONE'],
1184 $sync_batch += $batch_size;
1187 if ($sync_batch >= $primary_max)
1189 $sync_batch = -1;
1191 $db->sql_query('DELETE FROM ' . CONFIG_TABLE . "
1192 WHERE config_name = 'convert_progress' OR config_name = 'convert_options'");
1193 $db->sql_query('DELETE FROM ' . SESSIONS_TABLE);
1195 @unlink($phpbb_root_path . 'cache/data_global.php');
1196 cache_moderators();
1198 // And finally, add a note to the log
1199 add_log('admin', 'LOG_INSTALL_CONVERTED', $convert->convertor_data['forum_name'], $config['version']);
1201 $url = $this->p_master->module_url . "?mode={$this->mode}&amp;sub=final";
1203 $template->assign_vars(array(
1204 'L_SUBMIT' => $user->lang['FINAL_STEP'],
1205 'U_ACTION' => $url,
1208 $this->meta_refresh($url);
1209 return;
1211 else
1213 $sync_batch -= $batch_size;
1216 $step = '&amp;sync_batch=' . $sync_batch;
1218 // Save convertor Status
1219 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->options['table_prefix'], 'tag' => $convert->convertor_tag)), true);
1221 $url = $this->p_master->module_url . "?mode=$this->mode&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1223 $template->assign_vars(array(
1224 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
1225 'U_ACTION' => $url,
1228 $this->meta_refresh($url);
1229 return;
1233 * This function marks the end of conversion (jump=1)
1235 function jump($jump, $last_statement)
1237 global $template, $user, $db, $phpbb_root_path, $phpEx, $config, $cache;
1238 global $convert;
1240 $template->assign_block_vars('checks', array(
1241 'S_LEGEND' => true,
1242 'LEGEND' => $user->lang['PROCESS_LAST'],
1245 if ($jump == 1)
1247 // Execute 'last' statements/queries
1248 if (!empty($convert->convertor['execute_last']))
1250 if (!is_array($convert->convertor['execute_last']))
1252 eval($convert->convertor['execute_last']);
1254 else
1256 while ($last_statement < sizeof($convert->convertor['execute_last']))
1258 eval($convert->convertor['execute_last'][$last_statement]);
1260 $template->assign_block_vars('checks', array(
1261 'TITLE' => $convert->convertor['execute_last'][$last_statement],
1262 'RESULT' => $user->lang['DONE'],
1265 $last_statement++;
1266 $step = '&amp;jump=1&amp;last=' . $last_statement;
1268 // Save convertor Status
1269 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1271 $percentage = ($last_statement == 0) ? 0 : floor(100 / (sizeof($convert->convertor['execute_last']) / $last_statement));
1272 $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $last_statement, sizeof($convert->convertor['execute_last']), $percentage);
1273 $url = $this->p_master->module_url . "?mode={$this->mode}&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1275 $template->assign_vars(array(
1276 'L_SUBMIT' => $user->lang['CONTINUE_LAST'],
1277 'L_MESSAGE' => $msg,
1278 'U_ACTION' => $url,
1281 $this->meta_refresh($url);
1282 return;
1287 if (!empty($convert->convertor['query_last']))
1289 if (!is_array($convert->convertor['query_last']))
1291 $convert->convertor['query_last'] = array($convert->convertor['query_last']);
1294 foreach ($convert->convertor['query_last'] as $query_last)
1296 $db->sql_query($query_last);
1300 // Sanity check
1301 $db->sql_return_on_error(false);
1303 fix_empty_primary_groups();
1305 if (!isset($config['board_startdate']))
1307 $sql = 'SELECT MIN(user_regdate) AS board_startdate
1308 FROM ' . USERS_TABLE;
1309 $result = $db->sql_query($sql);
1310 $row = $db->sql_fetchrow($result);
1311 $db->sql_freeresult($result);
1313 if (($row['board_startdate'] < $config['board_startdate'] && $row['board_startdate'] > 0) || !isset($config['board_startdate']))
1315 set_config('board_startdate', $row['board_startdate']);
1316 $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_regdate = ' . $row['board_startdate'] . ' WHERE user_id = ' . ANONYMOUS);
1320 update_dynamic_config();
1322 $template->assign_block_vars('checks', array(
1323 'TITLE' => $user->lang['CLEAN_VERIFY'],
1324 'RESULT' => $user->lang['DONE'],
1327 $step = '&amp;jump=2';
1329 // Save convertor Status
1330 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1332 $url = $this->p_master->module_url . "?mode={$this->mode}&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1334 $template->assign_vars(array(
1335 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
1336 'U_ACTION' => $url,
1339 $this->meta_refresh($url);
1340 return;
1343 if ($jump == 2)
1345 $db->sql_query('UPDATE ' . USERS_TABLE . " SET user_permissions = ''");
1347 // TODO: sync() is likely going to bomb out on forums with a considerable amount of topics.
1348 // TODO: the sync function is able to handle FROM-TO values, we should use them here (batch processing)
1349 sync('forum');
1350 $cache->destroy('sql', FORUMS_TABLE);
1352 $template->assign_block_vars('checks', array(
1353 'TITLE' => $user->lang['SYNC_FORUMS'],
1354 'RESULT' => $user->lang['DONE'],
1357 $step = '&amp;jump=3';
1359 // Save convertor Status
1360 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1362 $url = $this->p_master->module_url . "?mode={$this->mode}&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1364 $template->assign_vars(array(
1365 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
1366 'U_ACTION' => $url,
1369 $this->meta_refresh($url);
1370 return;
1373 if ($jump == 3)
1375 update_topics_posted();
1377 $template->assign_block_vars('checks', array(
1378 'TITLE' => $user->lang['UPDATE_TOPICS_POSTED'],
1379 'RESULT' => $user->lang['DONE'],
1382 // Continue with synchronizing the forums...
1383 $step = '&amp;sync_batch=0';
1385 // Save convertor Status
1386 set_config('convert_progress', serialize(array('step' => $step, 'table_prefix' => $convert->src_table_prefix, 'tag' => $convert->convertor_tag)), true);
1388 $url = $this->p_master->module_url . "?mode={$this->mode}&amp;sub=in_progress&amp;tag={$convert->convertor_tag}$step";
1390 $template->assign_vars(array(
1391 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'],
1392 'U_ACTION' => $url,
1395 $this->meta_refresh($url);
1396 return;
1400 function build_insert_query(&$schema, &$sql_data, $current_table)
1402 global $db, $user;
1403 global $convert;
1405 // Can we use IGNORE with this DBMS?
1406 $sql_ignore = (strpos($db->sql_layer, 'mysql') === 0 && !defined('DEBUG_EXTRA')) ? 'IGNORE ' : '';
1407 $insert_query = 'INSERT ' . $sql_ignore . 'INTO ' . $schema['target'] . ' (';
1409 $aliases = array();
1411 $sql_data = array(
1412 'source_fields' => array(),
1413 'target_fields' => array(),
1414 'source_tables' => array(),
1415 'select_fields' => array(),
1418 foreach ($schema as $key => $val)
1420 // Example: array('group_name', 'extension_groups.group_name', 'htmlspecialchars'),
1421 if (is_int($key))
1423 if (!empty($val[0]))
1425 // Target fields
1426 $sql_data['target_fields'][$val[0]] = $key;
1427 $insert_query .= $val[0] . ', ';
1430 if (!is_array($val[1]))
1432 $val[1] = array($val[1]);
1435 foreach ($val[1] as $valkey => $value_1)
1437 // This should cover about any case:
1439 // table.field => SELECT table.field FROM table
1440 // table.field AS alias => SELECT table.field AS alias FROM table
1441 // table.field AS table2.alias => SELECT table2.field AS alias FROM table table2
1442 // table.field AS table2.field => SELECT table2.field FROM table table2
1444 if (preg_match('/^([a-z0-9_]+)\.([a-z0-9_]+)( +AS +(([a-z0-9_]+?)\.)?([a-z0-9_]+))?$/i', $value_1, $m))
1446 // There is 'AS ...' in the field names
1447 if (!empty($m[3]))
1449 $value_1 = ($m[2] == $m[6]) ? $m[1] . '.' . $m[2] : $m[1] . '.' . $m[2] . ' AS ' . $m[6];
1451 // Table alias: store it then replace the source table with it
1452 if (!empty($m[5]) && $m[5] != $m[1])
1454 $aliases[$m[5]] = $m[1];
1455 $value_1 = str_replace($m[1] . '.' . $m[2], $m[5] . '.' . $m[2], $value_1);
1458 else
1460 // No table alias
1461 $sql_data['source_tables'][$m[1]] = (empty($convert->src_table_prefix)) ? $m[1] : $convert->src_table_prefix . $m[1] . ' ' . $m[1];
1464 $sql_data['select_fields'][$value_1] = $value_1;
1465 $sql_data['source_fields'][$key][$valkey] = (!empty($m[6])) ? $m[6] : $m[2];
1469 else if ($key == 'where' || $key == 'group_by' || $key == 'order_by' || $key == 'having')
1471 if (@preg_match_all('/([a-z0-9_]+)\.([a-z0-9_]+)/i', $val, $m))
1473 foreach ($m[1] as $value)
1475 $sql_data['source_tables'][$value] = (empty($convert->src_table_prefix)) ? $value : $convert->src_table_prefix . $value . ' ' . $value;
1481 // Add the aliases to the list of tables
1482 foreach ($aliases as $alias => $table)
1484 $sql_data['source_tables'][$alias] = $convert->src_table_prefix . $table . ' ' . $alias;
1487 // 'left_join' => 'forums LEFT JOIN forum_prune ON forums.forum_id = forum_prune.forum_id',
1488 if (!empty($schema['left_join']))
1490 if (!is_array($schema['left_join']))
1492 $schema['left_join'] = array($schema['left_join']);
1495 foreach ($schema['left_join'] as $left_join)
1497 // This won't handle concatened LEFT JOINs
1498 if (!preg_match('/([a-z0-9_]+) LEFT JOIN ([a-z0-9_]+) A?S? ?([a-z0-9_]*?) ?(ON|USING)(.*)/i', $left_join, $m))
1500 $this->p_master->error(sprintf($user->lang['NOT_UNDERSTAND'], 'LEFT JOIN', $left_join, $current_table, $schema['target']), __LINE__, __FILE__);
1503 if (!empty($aliases[$m[2]]))
1505 if (!empty($m[3]))
1507 $this->p_master->error(sprintf($user->lang['NAMING_CONFLICT'], $m[2], $m[3], $schema['left_join']), __LINE__, __FILE__);
1510 $m[2] = $aliases[$m[2]];
1511 $m[3] = $m[2];
1514 $right_table = $convert->src_table_prefix . $m[2];
1515 if (!empty($m[3]))
1517 unset($sql_data['source_tables'][$m[3]]);
1519 else if ($m[2] != $m[1])
1521 unset($sql_data['source_tables'][$m[2]]);
1524 if (strpos($sql_data['source_tables'][$m[1]], "\nLEFT JOIN") !== false)
1526 $sql_data['source_tables'][$m[1]] = '(' . $sql_data['source_tables'][$m[1]] . ")\nLEFT JOIN $right_table";
1528 else
1530 $sql_data['source_tables'][$m[1]] .= "\nLEFT JOIN $right_table";
1533 if (!empty($m[3]))
1535 unset($sql_data['source_tables'][$m[3]]);
1536 $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[3];
1538 else if (!empty($convert->src_table_prefix))
1540 $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[2];
1542 $sql_data['source_tables'][$m[1]] .= ' ' . $m[4] . $m[5];
1546 // Remove ", " from the end of the insert query
1547 $insert_query = substr($insert_query, 0, -2) . ') VALUES ';
1549 return $insert_query;
1553 * Function for processing the currently handled row
1555 function process_row(&$schema, &$sql_data, &$insert_values)
1557 global $template, $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache;
1558 global $convert, $convert_row;
1560 $sql_flag = false;
1562 foreach ($schema as $key => $fields)
1564 // We are only interested in the lines with:
1565 // array('comment', 'attachments_desc.comment', 'htmlspecialchars'),
1566 if (is_int($key))
1568 if (!is_array($fields[1]))
1570 $fields[1] = array($fields[1]);
1573 $firstkey_set = false;
1574 $firstkey = 0;
1576 foreach ($fields[1] as $inner_key => $inner_value)
1578 if (!$firstkey_set)
1580 $firstkey = $inner_key;
1581 $firstkey_set = true;
1584 $src_field = isset($sql_data['source_fields'][$key][$inner_key]) ? $sql_data['source_fields'][$key][$inner_key] : '';
1586 if (!empty($src_field))
1588 $fields[1][$inner_key] = $convert->row[$src_field];
1592 if (!empty($fields[0]))
1594 // We have a target field, if we haven't set $sql_flag yet it will be set to TRUE.
1595 // If a function has already set it to FALSE it won't change it.
1596 if ($sql_flag === false)
1598 $sql_flag = true;
1601 // No function assigned?
1602 if (empty($fields[2]))
1604 $value = $fields[1][$firstkey];
1606 else if (is_array($fields[2]))
1608 // Execute complex function/eval/typecast
1609 $value = $fields[1];
1611 foreach ($fields[2] as $type => $execution)
1613 if (strpos($type, 'typecast') === 0)
1615 if (!is_array($value))
1617 $value = array($value);
1619 $value = $value[0];
1620 settype($value, $execution);
1622 else if (strpos($type, 'function') === 0)
1624 if (!is_array($value))
1626 $value = array($value);
1629 $value = call_user_func_array($execution, $value);
1631 else if (strpos($type, 'execute') === 0)
1633 if (!is_array($value))
1635 $value = array($value);
1638 $execution = str_replace('{RESULT}', '$value', $execution);
1639 $execution = str_replace('{VALUE}', '$value', $execution);
1640 eval($execution);
1644 else
1646 $value = call_user_func_array($fields[2], $fields[1]);
1649 if (is_null($value))
1651 $value = '';
1654 $insert_values[] = $db->_sql_validate_value($value);
1656 else if (!empty($fields[2]))
1658 if (is_array($fields[2]))
1660 // Execute complex function/eval/typecast
1661 $value = '';
1663 foreach ($fields[2] as $type => $execution)
1665 if (strpos($type, 'typecast') === 0)
1667 $value = settype($value, $execution);
1669 else if (strpos($type, 'function') === 0)
1671 if (!is_array($value))
1673 $value = array($value);
1676 $value = call_user_func_array($execution, $value);
1678 else if (strpos($type, 'execute') === 0)
1680 if (!is_array($value))
1682 $value = array($value);
1685 $execution = str_replace('{RESULT}', '$value', $execution);
1686 $execution = str_replace('{VALUE}', '$value', $execution);
1687 eval($execution);
1691 else
1693 call_user_func_array($fields[2], $fields[1]);
1699 return $sql_flag;
1703 * Own meta refresh function to be able to change the global time used
1705 function meta_refresh($url)
1707 global $convert;
1709 if ($convert->options['refresh'])
1711 meta_refresh(5, $url);
1716 * The information below will be used to build the input fields presented to the user
1718 var $convert_options = array(
1719 'legend1' => 'SPECIFY_OPTIONS',
1720 'src_table_prefix' => array('lang' => 'TABLE_PREFIX', 'type' => 'text:25:100', 'explain' => false),
1721 //'src_url' => array('lang' => 'FORUM_ADDRESS', 'type' => 'text:50:100', 'explain' => true),
1722 'forum_path' => array('lang' => 'FORUM_PATH', 'type' => 'text:25:100', 'explain' => true),
1723 'refresh' => array('lang' => 'REFRESH_PAGE', 'type' => 'radio:yes_no', 'explain' => true),