diff options
author | Mate Bartus <mate.bartus@gmail.com> | 2015-10-28 15:00:11 +0100 |
---|---|---|
committer | Mate Bartus <mate.bartus@gmail.com> | 2016-01-29 00:03:44 +0100 |
commit | a649768e17d25bcf55ae539420abe4eb4b7a1ef1 (patch) | |
tree | f8441c9c77054d6a58bd1e8befa8842ef180be4a | |
parent | 10756f3f87efac29448a48207548d5b71a3206b9 (diff) | |
download | forums-a649768e17d25bcf55ae539420abe4eb4b7a1ef1.tar forums-a649768e17d25bcf55ae539420abe4eb4b7a1ef1.tar.gz forums-a649768e17d25bcf55ae539420abe4eb4b7a1ef1.tar.bz2 forums-a649768e17d25bcf55ae539420abe4eb4b7a1ef1.tar.xz forums-a649768e17d25bcf55ae539420abe4eb4b7a1ef1.zip |
[ticket/14262] Move convertor to controller
PHPBB3-14262
17 files changed, 3082 insertions, 3 deletions
diff --git a/phpBB/adm/style/installer_convert.html b/phpBB/adm/style/installer_convert.html new file mode 100644 index 0000000000..8f1c51a1cb --- /dev/null +++ b/phpBB/adm/style/installer_convert.html @@ -0,0 +1,79 @@ +<!-- INCLUDE installer_header.html --> +<!-- IF TITLE --><h1>{TITLE}</h1><!-- ENDIF --> +<!-- IF S_ERROR_BOX --> +<div class="errorbox"> + <h3>{ERROR_TITLE}</h3> + <p>{ERROR_MSG}</p> +</div> +<!-- ENDIF --> +<!-- IF BODY --><p>{BODY}</p><!-- ENDIF --> +<!-- IF CONTENT -->{CONTENT}<!-- ENDIF --> +<!-- IF S_CONV_IN_PROGRESS --> +<p class="centered-text"><a href="{U_ACTION}" class="button1<!-- IF S_REFRESH --> disabled<!-- ENDIF -->"<!-- IF S_REFRESH --> onclick="return false;"<!-- ENDIF -->>{L_SUBMIT}</a></p> +<!-- ENDIF --> +<!-- IF S_CONTINUE --> +<div id="download-wrapper" class="download-box"> + <p class="centered-text"> + <a href="{U_NEW_ACTION}" class="button1">{L_CONVERT_NEW_CONVERSION}</a> + <a href="{U_CONTINUE_ACTION}" class="button1">{L_CONTINUE_OLD_CONVERSION}</a> + </p> +</div> +<!-- ENDIF --> +<!-- IF S_LIST --> +<table class="table1"> + <caption>{L_AVAILABLE_CONVERTORS}</caption> + <col class="col1" /><col class="col2" /><col class="col1" /><col class="col2" /> + <thead> + <tr> + <th>{L_SOFTWARE}</th> + <th>{L_VERSION}</th> + <th>{L_AUTHOR}</th> + <th>{L_CONVERT_OPTIONS}</th> + </tr> + </thead> + <tbody> + <!-- IF .convertors --> + <!-- BEGIN convertors --> + <tr> + <td>{convertors.SOFTWARE}</td> + <td>{convertors.VERSION}</td> + <td>{convertors.AUTHOR}</td> + <td><a href="{convertors.U_CONVERT}">{L_CONVERT}</a></td> + </tr> + <!-- END convertors --> + <!-- ELSE --> + <tr> + <td>{L_NO_CONVERTORS}</td> + <td>-</td> + <td>-</td> + <td>-</td> + </tr> + <!-- ENDIF --> + </tbody> +</table> +<!-- ENDIF --> +<!-- IF .checks --> +<fieldset> + + <!-- BEGIN checks --> + <!-- IF checks.S_LEGEND --> + <!-- IF not checks.S_FIRST_ROW --> +</fieldset> + +<fieldset> + <!-- ENDIF --> + <legend>{checks.LEGEND}</legend> + <!-- IF checks.LEGEND_EXPLAIN --><p>{checks.LEGEND_EXPLAIN}</p><!-- ENDIF --> + <!-- ELSE --> + + <dl> + <dt><label>{checks.TITLE}{L_COLON}</label><!-- IF checks.S_EXPLAIN --><br /><span class="explain">{checks.TITLE_EXPLAIN}</span><!-- ENDIF --></dt> + <dd>{checks.RESULT}</dd> + </dl> + <!-- ENDIF --> + <!-- END checks --> + +</fieldset> +<!-- ENDIF --> +<!-- INCLUDEJS {T_ASSETS_PATH}/javascript/installer.js --> +<!-- INCLUDE installer_footer.html --> diff --git a/phpBB/adm/style/installer_convert_old.html b/phpBB/adm/style/installer_convert_old.html new file mode 100644 index 0000000000..75cc151c9e --- /dev/null +++ b/phpBB/adm/style/installer_convert_old.html @@ -0,0 +1,134 @@ +<!-- INCLUDE installer_header.html --> + +<!-- IF S_NOT_INSTALLED --> + + <h1>{TITLE}</h1> + + <p>{BODY}</p> + +<!-- ELSE --> + + <form id="install_convert" method="post" action="{U_ACTION}"> + + <h1>{TITLE}</h1> + + <p>{BODY}</p> + + <!-- IF S_ERROR_BOX --> + <div class="errorbox"> + <h3>{ERROR_TITLE}</h3> + <p>{ERROR_MSG}</p> + </div> + <!-- ENDIF --> + + <!-- IF S_LIST --> + <table class="table1"> + <caption>{L_AVAILABLE_CONVERTORS}</caption> + <col class="col1" /><col class="col2" /><col class="col1" /><col class="col2" /> + <thead> + <tr> + <th>{L_SOFTWARE}</th> + <th>{L_VERSION}</th> + <th>{L_AUTHOR}</th> + <th>{L_OPTIONS}</th> + </tr> + </thead> + <tbody> + <!-- IF .convertors --> + <!-- BEGIN convertors --> + <tr> + <td>{convertors.SOFTWARE}</td> + <td>{convertors.VERSION}</td> + <td>{convertors.AUTHOR}</td> + <td><a href="{convertors.U_CONVERT}">{L_CONVERT}</a></td> + </tr> + <!-- END convertors --> + <!-- ELSE --> + <tr> + <td>{L_NO_CONVERTORS}</td> + <td>-</td> + <td>-</td> + <td>-</td> + </tr> + <!-- ENDIF --> + </tbody> + </table> + <!-- ENDIF --> + + <!-- IF S_CONTINUE --> + </form> + + <fieldset class="submit-buttons"> + <form method="post" action="{U_NEW_ACTION}"> + <input class="button1" type="submit" name="submit_new" value="{L_NEW}" /> + </form> + <br /> + <form method="post" action="{U_CONTINUE_ACTION}"> + <input class="button1" type="submit" name="submit_cont" value="{L_CONTINUE}" /> + </form> + </fieldset> + + <form method="post" action="{U_ACTION}"> + <!-- ENDIF --> + + <!-- IF .checks --> + <fieldset> + + <!-- BEGIN checks --> + <!-- IF checks.S_LEGEND --> + <!-- IF not checks.S_FIRST_ROW --> + </fieldset> + + <fieldset> + <!-- ENDIF --> + <legend>{checks.LEGEND}</legend> + <!-- IF checks.LEGEND_EXPLAIN --><p>{checks.LEGEND_EXPLAIN}</p><!-- ENDIF --> + <!-- ELSE --> + + <dl> + <dt><label>{checks.TITLE}{L_COLON}</label><!-- IF checks.S_EXPLAIN --><br /><span class="explain">{checks.TITLE_EXPLAIN}</span><!-- ENDIF --></dt> + <dd>{checks.RESULT}</dd> + </dl> + <!-- ENDIF --> + <!-- END checks --> + + </fieldset> + <!-- ENDIF --> + + <!-- IF .options --> + <fieldset> + + <!-- BEGIN options --> + <!-- IF options.S_LEGEND --> + <!-- IF not options.S_FIRST_ROW --> + </fieldset> + + <fieldset> + <!-- ENDIF --> + <legend>{options.LEGEND}</legend> + <!-- ELSE --> + + <dl> + <dt><label for="{options.KEY}">{options.TITLE}{L_COLON}</label><!-- IF options.S_EXPLAIN --><br /><span class="explain">{options.TITLE_EXPLAIN}</span><!-- ENDIF --></dt> + <dd>{options.CONTENT}</dd> + </dl> + + <!-- ENDIF --> + <!-- END options --> + + </fieldset> + <!-- ENDIF --> + + <!-- IF L_SUBMIT --> + <!-- IF L_MESSAGE --><p>{L_MESSAGE}</p><!-- ENDIF --> + + <fieldset class="submit-buttons"> + {S_HIDDEN} + <!-- IF L_SUBMIT --><input class="button1<!-- IF S_REFRESH --> disabled<!-- ENDIF -->" type="submit" id="submit" <!-- IF S_REFRESH -->disabled="disabled" <!-- ELSE --> onclick="this.className = 'button1 disabled';" onsubmit="this.disabled = 'disabled';" <!-- ENDIF -->name="submit" value="{L_SUBMIT}" /><!-- ENDIF --> + </fieldset> + <!-- ENDIF --> + + </form> +<!-- ENDIF --> + +<!-- INCLUDE installer_footer.html --> diff --git a/phpBB/assets/javascript/installer.js b/phpBB/assets/javascript/installer.js index c5909556c6..7bb2bb426d 100644 --- a/phpBB/assets/javascript/installer.js +++ b/phpBB/assets/javascript/installer.js @@ -236,6 +236,22 @@ } } + // Redirects user + function redirect(url, use_ajax) { + if (use_ajax) { + resetPolling(); + + var xhReq = createXhrObject(); + xhReq.open('GET', url, true); + xhReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhReq.send(); + + startPolling(xhReq); + } else { + window.location.href = url; + } + } + /** * Parse messages from the response object * @@ -302,6 +318,10 @@ if (responseObject.hasOwnProperty('refresh')) { refreshRequested = true; } + + if (responseObject.hasOwnProperty('redirect')) { + redirect(responseObject.redirect.url, responseObject.redirect.use_ajax); + } } /** diff --git a/phpBB/config/installer/container/services_install_controller.yml b/phpBB/config/installer/container/services_install_controller.yml index 5be28c5746..5688c61db9 100644 --- a/phpBB/config/installer/container/services_install_controller.yml +++ b/phpBB/config/installer/container/services_install_controller.yml @@ -49,3 +49,18 @@ services: class: phpbb\install\controller\archive_download arguments: - '@installer.helper.config' + + phpbb.installer.controller.convert: + class: phpbb\convert\controller\convertor + arguments: + - '@installer.helper.container_factory' + - '@installer.helper.database' + - '@phpbb.installer.controller.helper' + - '@installer.helper.install_helper' + - '@installer.helper.iohandler_factory' + - '@language' + - '@installer.navigation.provider' + - '@request' + - '@template' + - '%core.root_path%' + - '%core.php_ext%' diff --git a/phpBB/config/installer/container/services_install_navigation.yml b/phpBB/config/installer/container/services_install_navigation.yml index 301d6f3434..4fc6abb6eb 100644 --- a/phpBB/config/installer/container/services_install_navigation.yml +++ b/phpBB/config/installer/container/services_install_navigation.yml @@ -33,3 +33,10 @@ services: tags: - { name: installer.navigation } + installer.navigation.convertor_navigation: + class: phpbb\install\helper\navigation\convertor_navigation + arguments: + - '@installer.helper.install_helper' + scope: prototype + tags: + - { name: installer.navigation } diff --git a/phpBB/config/installer/routing/installer.yml b/phpBB/config/installer/routing/installer.yml index 6190799737..1961ba8ed6 100644 --- a/phpBB/config/installer/routing/installer.yml +++ b/phpBB/config/installer/routing/installer.yml @@ -35,3 +35,28 @@ phpbb_installer_update_conflict_download: path: /download/conflict defaults: _controller: phpbb.installer.controller.file_downloader:conflict_archive + +phpbb_convert_intro: + path: /convert/{start_new} + defaults: + _controller: phpbb.installer.controller.convert:intro + start_new: 0 + +phpbb_convert_settings: + path: /convert/settings/{convertor} + defaults: + _controller: phpbb.installer.controller.convert:settings + requirements: + converter: "[a-zA-Z0-9_]+" + +phpbb_convert_convert: + path: /convert/in_progress/{converter} + defaults: + _controller: phpbb.installer.controller.convert:convert + requirements: + converter: "[a-zA-Z0-9_]+" + +phpbb_convert_finish: + path: /convert/finished + defaults: + _controller: phpbb.installer.controller.convert:finish diff --git a/phpBB/install/convert/controller/convertor.php b/phpBB/install/convert/controller/convertor.php new file mode 100644 index 0000000000..c9d78bd438 --- /dev/null +++ b/phpBB/install/convert/controller/convertor.php @@ -0,0 +1,871 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\convert\controller; + +use phpbb\exception\http_exception; +use phpbb\install\controller\helper; +use phpbb\install\helper\container_factory; +use phpbb\install\helper\database; +use phpbb\install\helper\install_helper; +use phpbb\install\helper\iohandler\factory; +use phpbb\install\helper\iohandler\iohandler_interface; +use phpbb\install\helper\navigation\navigation_provider; +use phpbb\language\language; +use phpbb\request\request_interface; +use phpbb\template\template; +use Symfony\Component\HttpFoundation\StreamedResponse; + +/** + * Controller for forum convertors + * + * WARNING: This file did not meant to be present in a production environment, so moving + * this file to a location which is accessible after board installation might + * lead to security issues. + */ +class convertor +{ + /** + * @var \phpbb\cache\driver\driver_interface + */ + protected $cache; + + /** + * @var \phpbb\config\db + */ + protected $config; + + /** + * @var \phpbb\config_php_file + */ + protected $config_php_file; + + /** + * @var string + */ + protected $config_table; + + /** + * @var helper + */ + protected $controller_helper; + + /** + * @var database + */ + protected $db_helper; + + /** + * @var \phpbb\db\driver\driver_interface + */ + protected $db; + + /** + * @var install_helper + */ + protected $install_helper; + + /** + * @var iohandler_interface + */ + protected $iohandler; + + /** + * @var language + */ + protected $language; + + /** + * @var navigation_provider + */ + protected $navigation_provider; + + /** + * @var request_interface + */ + protected $request; + + /** + * @var string + */ + protected $session_keys_table; + + /** + * @var string + */ + protected $session_table; + + /** + * @var template + */ + protected $template; + + /** + * @var string + */ + protected $phpbb_root_path; + + /** + * @var string + */ + protected $php_ext; + + /** + * Constructor + * + * @param container_factory $container + * @param database $db_helper + * @param helper $controller_helper + * @param install_helper $install_helper + * @param factory $iohandler + * @param language $language + * @param navigation_provider $nav + * @param request_interface $request + * @param template $template + * @param string $phpbb_root_path + * @param string $php_ext + */ + public function __construct(container_factory $container, database $db_helper, helper $controller_helper, install_helper $install_helper, factory $iohandler, language $language, navigation_provider $nav, request_interface $request, template $template, $phpbb_root_path, $php_ext) + { + $this->controller_helper = $controller_helper; + $this->db_helper = $db_helper; + $this->install_helper = $install_helper; + $this->language = $language; + $this->navigation_provider = $nav; + $this->request = $request; + $this->template = $template; + $this->phpbb_root_path = $phpbb_root_path; + $this->php_ext = $php_ext; + + $iohandler->set_environment('ajax'); + $this->iohandler = $iohandler->get(); + + if (!$this->install_helper->is_phpbb_installed() || !defined('IN_INSTALL')) + { + throw new http_exception(403, 'INSTALL_PHPBB_NOT_INSTALLED'); + } + + $this->controller_helper->handle_language_select(); + + $this->cache = $container->get('cache.driver'); + $this->config = $container->get('config'); + $this->config_php_file = new \phpbb\config_php_file($this->phpbb_root_path, $this->php_ext); + $this->db = $container->get('dbal.conn.driver'); + + $this->config_table = $container->get_parameter('tables.config'); + $this->session_keys_table = $container->get_parameter('tables.sessions_keys'); + $this->session_table = $container->get_parameter('tables.sessions'); + } + + /** + * Render the intro page + * + * @param bool|int $start_new Whether or not to force to start a new convertor + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function intro($start_new) + { + $this->setup_navigation('intro'); + + if ($start_new) + { + if ($this->request->is_ajax()) + { + $response = new StreamedResponse(); + $iohandler = $this->iohandler; + $url = $this->controller_helper->route('phpbb_convert_intro', array('start_new' => 'new')); + $response->setCallback(function() use ($iohandler, $url) { + $iohandler->redirect($url); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + + $this->config['convert_progress'] = ''; + $this->config['convert_db_server'] = ''; + $this->config['convert_db_user'] = ''; + $this->db->sql_query('DELETE FROM ' . $this->config_table . " + WHERE config_name = 'convert_progress' + OR config_name = 'convert_db_server' + OR config_name = 'convert_db_user'" + ); + } + + // Let's see if there is a conversion in the works... + $options = array(); + if (!empty($this->config['convert_progress']) && + !empty($this->config['convert_db_server']) && + !empty($this->config['convert_db_user']) && + !empty($this->config['convert_options'])) + { + $options = unserialize($this->config['convert_progress']); + $options = array_merge($options, + unserialize($this->config['convert_db_server']), + unserialize($this->config['convert_db_user']), + unserialize($this->config['convert_options']) + ); + } + + // This information should have already been checked once, but do it again for safety + if (!empty($options) && !empty($options['tag']) && + isset($options['dbms']) && + isset($options['dbhost']) && + isset($options['dbport']) && + isset($options['dbuser']) && + isset($options['dbpasswd']) && + isset($options['dbname']) && + isset($options['table_prefix'])) + { + $this->template->assign_vars(array( + 'TITLE' => $this->language->lang('CONTINUE_CONVERT'), + 'BODY' => $this->language->lang('CONTINUE_CONVERT_BODY'), + 'S_CONTINUE' => true, + 'U_NEW_ACTION' => $this->controller_helper->route('phpbb_convert_intro', array('start_new' => 'new')), + 'U_CONTINUE_ACTION' => $this->controller_helper->route('phpbb_convert_convert', array('converter' => $options['tag'])), + )); + + return $this->controller_helper->render('installer_convert.html', 'CONTINUE_CONVERT', true); + } + + return $this->render_convert_list(); + } + + /** + * Obtain convertor settings + * + * @param string $convertor Name of the convertor + * + * @return \Symfony\Component\HttpFoundation\Response|StreamedResponse + */ + public function settings($convertor) + { + $this->setup_navigation('settings'); + + require_once ($this->phpbb_root_path . 'includes/constants.' . $this->php_ext); + require_once ($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext); + + // Include convertor if available + $convertor_file_path = $this->phpbb_root_path . 'install/convertors/convert_' . $convertor . '.' . $this->php_ext; + if (!file_exists($convertor_file_path)) + { + if ($this->request->is_ajax()) + { + $response = new StreamedResponse(); + $ref = $this; + $response->setCallback(function() use ($ref) { + $ref->render_error('CONVERT_NOT_EXIST'); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + + $this->render_error('CONVERT_NOT_EXIST'); + return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true); + } + + $get_info = true; + $phpbb_root_path = $this->phpbb_root_path; // These globals are required + $phpEx = $this->php_ext; // See above + include_once ($convertor_file_path); + + // The test_file is a file that should be present in the location of the old board. + if (!isset($test_file)) + { + if ($this->request->is_ajax()) + { + $response = new StreamedResponse(); + $ref = $this; + $response->setCallback(function() use ($ref) { + $ref->render_error('DEV_NO_TEST_FILE'); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + + $this->render_error('DEV_NO_TEST_FILE'); + return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true); + } + + if ($this->request->variable('submit', false)) + { + // It must be an AJAX request at this point + $response = new StreamedResponse(); + $ref = $this; + $response->setCallback(function() use ($ref, $convertor) { + $ref->proccess_settings_form($convertor); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + else + { + $this->template->assign_vars(array( + 'U_ACTION' => $this->controller_helper->route('phpbb_convert_settings', array( + 'convertor' => $convertor, + )) + )); + + if ($this->request->is_ajax()) + { + $response = new StreamedResponse(); + $ref = $this; + $response->setCallback(function() use ($ref) { + $ref->render_settings_form(); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + + $this->render_settings_form(); + } + + return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true); + } + + /** + * Run conversion + */ + public function convert($converter) + { + $this->setup_navigation('convert'); + + if ($this->request->is_ajax()) + { + $route = $this->controller_helper->route('phpbb_convert_convert', array('converter' => $converter)); + $response = new StreamedResponse(); + $ref = $this; + $response->setCallback(function() use ($ref, $route) { + $ref->redirect_to_html($route); + }); + $response->headers->set('X-Accel-Buffering', 'no'); + + return $response; + } + + $convertor = new \phpbb\convert\convertor($this->template, $this->controller_helper); + $convertor->convert_data($converter); + + return $this->controller_helper->render('installer_convert.html', 'STAGE_IN_PROGRESS'); + } + + /** + * Render the final page of the convertor + */ + public function finish() + { + $this->setup_navigation('finish'); + + $this->template->assign_vars(array( + 'TITLE' => $this->language->lang('CONVERT_COMPLETE'), + 'BODY' => $this->language->lang('CONVERT_COMPLETE_EXPLAIN'), + )); + + // If we reached this step (conversion completed) we want to purge the cache and log the user out. + // This is for making sure the session get not screwed due to the 3.0.x users table being completely new. + $this->cache->purge(); + + require_once($this->phpbb_root_path . 'includes/constants.' . $this->php_ext); + require_once($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext); + + $sql = 'SELECT config_value + FROM ' . $this->config_table . ' + WHERE config_name = \'search_type\''; + $result = $this->db->sql_query($sql); + + if ($this->db->sql_fetchfield('config_value') != 'fulltext_mysql') + { + $this->template->assign_vars(array( + 'S_ERROR_BOX' => true, + 'ERROR_TITLE' => $this->language->lang('SEARCH_INDEX_UNCONVERTED'), + 'ERROR_MSG' => $this->language->lang('SEARCH_INDEX_UNCONVERTED_EXPLAIN'), + )); + } + + $this->db->sql_freeresult($result); + + switch ($this->db->get_sql_layer()) + { + case 'sqlite': + case 'sqlite3': + $this->db->sql_query('DELETE FROM ' . $this->session_keys_table); + $this->db->sql_query('DELETE FROM ' . $this->session_table); + break; + + default: + $this->db->sql_query('TRUNCATE TABLE ' . $this->session_keys_table); + $this->db->sql_query('TRUNCATE TABLE ' . $this->session_table); + break; + } + + return $this->controller_helper->render('installer_convert.html', 'CONVERT_COMPLETE'); + } + + /** + * Validates settings form + * + * @param string $convertor + */ + public function proccess_settings_form($convertor) + { + global $phpbb_root_path, $phpEx, $get_info; + + $phpbb_root_path = $this->phpbb_root_path; + $phpEx = $this->php_ext; + $get_info = true; + + require_once($this->phpbb_root_path . 'includes/constants.' . $this->php_ext); + require_once($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext); + + // Include convertor if available + $convertor_file_path = $this->phpbb_root_path . 'install/convertors/convert_' . $convertor . '.' . $this->php_ext; + include ($convertor_file_path); + + // We expect to have an AJAX request here + $src_dbms = $this->request->variable('src_dbms', $convertor_data['dbms']); + $src_dbhost = $this->request->variable('src_dbhost', $convertor_data['dbhost']); + $src_dbport = $this->request->variable('src_dbport', $convertor_data['dbport']); + $src_dbuser = $this->request->variable('src_dbuser', $convertor_data['dbuser']); + $src_dbpasswd = $this->request->variable('src_dbpasswd', $convertor_data['dbpasswd']); + $src_dbname = $this->request->variable('src_dbname', $convertor_data['dbname']); + $src_table_prefix = $this->request->variable('src_table_prefix', $convertor_data['table_prefix']); + $forum_path = $this->request->variable('forum_path', $convertor_data['forum_path']); + $refresh = $this->request->variable('refresh', 1); + + // Default URL of the old board + // @todo Are we going to use this for attempting to convert URL references in posts, or should we remove it? + // -> We should convert old urls to the new relative urls format + // $src_url = $request->variable('src_url', 'Not in use at the moment'); + + // strip trailing slash from old forum path + $forum_path = (strlen($forum_path) && $forum_path[strlen($forum_path) - 1] == '/') ? substr($forum_path, 0, -1) : $forum_path; + + $error = array(); + if (!file_exists($this->phpbb_root_path . $forum_path . '/' . $test_file)) + { + $error[] = $this->language->lang('COULD_NOT_FIND_PATH', $forum_path); + } + + $connect_test = false; + $available_dbms = $this->db_helper->get_available_dbms(false, true, true); + if (!isset($available_dbms[$src_dbms]) || !$available_dbms[$src_dbms]['AVAILABLE']) + { + $error[] = $this->language->lang('INST_ERR_NO_DB'); + } + else + { + $connect_test = $this->db_helper->check_database_connection($src_dbms, $src_dbhost, $src_dbport, $src_dbuser, $src_dbpasswd, $src_dbname, $src_table_prefix); + } + + extract($this->config_php_file->get_all()); + + // The forum prefix of the old and the new forum can only be the same if two different databases are used. + if ($src_table_prefix === $table_prefix && $src_dbms === $dbms && $src_dbhost === $dbhost && $src_dbport === $dbport && $src_dbname === $dbname) + { + $error[] = $this->language->lang('TABLE_PREFIX_SAME', $src_table_prefix); + } + + if (!$connect_test) + { + $error[] = $this->language->lang('INST_ERR_DB_CONNECT'); + } + + $src_dbms = $this->config_php_file->convert_30_dbms_to_31($src_dbms); + + // Check table prefix + if (empty($error)) + { + // initiate database connection to old db if old and new db differ + global $src_db, $same_db; + $src_db = $same_db = false; + + if ($src_dbms != $dbms || $src_dbhost != $dbhost || $src_dbport != $dbport || $src_dbname != $dbname || $src_dbuser != $dbuser) + { + /** @var \phpbb\db\driver\driver_interface $src_db */ + $src_db = new $src_dbms(); + $src_db->sql_connect($src_dbhost, $src_dbuser, htmlspecialchars_decode($src_dbpasswd), $src_dbname, $src_dbport, false, true); + $same_db = false; + } + else + { + $src_db = $this->db; + $same_db = true; + } + + $src_db->sql_return_on_error(true); + $this->db->sql_return_on_error(true); + + // Try to select one row from the first table to see if the prefix is OK + $result = $src_db->sql_query_limit('SELECT * FROM ' . $src_table_prefix . $tables[0], 1); + + if (!$result) + { + $prefixes = array(); + + $db_tools_factory = new \phpbb\db\tools\factory(); + $db_tools = $db_tools_factory->get($src_db); + $tables_existing = $db_tools->sql_list_tables(); + $tables_existing = array_map('strtolower', $tables_existing); + foreach ($tables_existing as $table_name) + { + compare_table($tables, $table_name, $prefixes); + } + unset($tables_existing); + + foreach ($prefixes as $prefix => $count) + { + if ($count >= sizeof($tables)) + { + $possible_prefix = $prefix; + break; + } + } + + $msg = ''; + if (!empty($convertor_data['table_prefix'])) + { + $msg .= $this->language->lang_array('DEFAULT_PREFIX_IS', array($convertor_data['forum_name'], $convertor_data['table_prefix'])); + } + + if (!empty($possible_prefix)) + { + $msg .= '<br />'; + $msg .= ($possible_prefix == '*') ? $this->language->lang('BLANK_PREFIX_FOUND') : $this->language->lang_array('PREFIX_FOUND', array($possible_prefix)); + $src_table_prefix = ($possible_prefix == '*') ? '' : $possible_prefix; + } + + $error[] = $msg; + } + + $src_db->sql_freeresult($result); + $src_db->sql_return_on_error(false); + } + + if (empty($error)) + { + // Save convertor Status + $this->config->set('convert_progress', serialize(array( + 'step' => '', + 'table_prefix' => $src_table_prefix, + 'tag' => $convertor, + )), false); + $this->config->set('convert_db_server', serialize(array( + 'dbms' => $src_dbms, + 'dbhost' => $src_dbhost, + 'dbport' => $src_dbport, + 'dbname' => $src_dbname, + )), false); + $this->config->set('convert_db_user', serialize(array( + 'dbuser' => $src_dbuser, + 'dbpasswd' => $src_dbpasswd, + )), false); + + // Save options + $this->config->set('convert_options', serialize(array( + 'forum_path' => $this->phpbb_root_path . $forum_path, + 'refresh' => $refresh + )), false); + + $url = $this->controller_helper->route('phpbb_convert_convert', array('converter' => $convertor)); + $this->iohandler->redirect($url); + $this->iohandler->send_response(); + } + else + { + $this->render_settings_form($error); + } + } + + /** + * Renders settings form + * + * @param array $error Array of errors + */ + public function render_settings_form($error = array()) + { + foreach ($error as $msg) + { + $this->iohandler->add_error_message($msg); + } + + $dbms_options = array(); + foreach ($this->db_helper->get_available_dbms() as $dbms_key => $dbms_array) + { + $dbms_options[] = array( + 'value' => $dbms_key, + 'label' => 'DB_OPTION_' . strtoupper($dbms_key), + ); + } + + $form_title = 'SPECIFY_OPTIONS'; + $form_data = array( + 'src_dbms' => array( + 'label' => 'DBMS', + 'type' => 'select', + 'options' => $dbms_options, + ), + 'src_dbhost' => array( + 'label' => 'DB_HOST', + 'description' => 'DB_HOST_EXPLAIN', + 'type' => 'text', + ), + 'src_dbport' => array( + 'label' => 'DB_PORT', + 'description' => 'DB_PORT_EXPLAIN', + 'type' => 'text', + ), + 'src_dbname' => array( + 'label' => 'DB_NAME', + 'type' => 'text', + ), + 'src_dbuser' => array( + 'label' => 'DB_USERNAME', + 'type' => 'text', + ), + 'src_dbpasswd' => array( + 'label' => 'DB_PASSWORD', + 'type' => 'password', + ), + 'src_table_prefix' => array( + 'label' => 'TABLE_PREFIX', + 'description' => 'TABLE_PREFIX_EXPLAIN', + 'type' => 'text', + ), + 'forum_path' => array( + 'label' => 'FORUM_PATH', + 'description' => 'FORUM_PATH_EXPLAIN', + 'type' => 'text', + ), + 'refresh' => array( + 'label' => 'REFRESH_PAGE', + 'description' => 'REFRESH_PAGE_EXPLAIN', + 'type' => 'radio', + 'options' => array( + array( + 'value' => 0, + 'label' => 'NO', + 'selected' => true, + ), + array( + 'value' => 1, + 'label' => 'YES', + 'selected' => false, + ), + ), + ), + 'submit' => array( + 'label' => 'SUBMIT', + 'type' => 'submit', + ), + ); + + if ($this->request->is_ajax()) + { + $this->iohandler->add_user_form_group($form_title, $form_data); + $this->iohandler->send_response(); + } + else + { + $rendered_form = $this->iohandler->generate_form_render_data($form_title, $form_data); + + $this->template->assign_vars(array( + 'TITLE' => $this->language->lang('STAGE_SETTINGS'), + 'CONTENT' => $rendered_form, + )); + } + } + + /** + * Render the list of available convertors + * + * @return \Symfony\Component\HttpFoundation\Response + */ + protected function render_convert_list() + { + $this->template->assign_vars(array( + 'TITLE' => $this->language->lang('CONVERT_INTRO'), + 'BODY' => $this->language->lang('CONVERT_INTRO_BODY'), + 'S_LIST' => true, + )); + + $convertors = $sort = array(); + $get_info = true; // Global flag + + $handle = @opendir($this->phpbb_root_path . 'install/convertors/'); + + if (!$handle) + { + die('Unable to access the convertors directory'); + } + + while ($entry = readdir($handle)) + { + if (preg_match('/^convert_([a-z0-9_]+).' . $this->php_ext . '$/i', $entry, $m)) + { + $phpbb_root_path = $this->phpbb_root_path; // These globals are required + $phpEx = $this->php_ext; // See above + include_once($this->phpbb_root_path . 'install/convertors/' . $entry); + if (isset($convertor_data)) + { + $sort[strtolower($convertor_data['forum_name'])] = sizeof($convertors); + + $convertors[] = array( + 'tag' => $m[1], + 'forum_name' => $convertor_data['forum_name'], + 'version' => $convertor_data['version'], + 'dbms' => $convertor_data['dbms'], + 'dbhost' => $convertor_data['dbhost'], + 'dbport' => $convertor_data['dbport'], + 'dbuser' => $convertor_data['dbuser'], + 'dbpasswd' => $convertor_data['dbpasswd'], + 'dbname' => $convertor_data['dbname'], + 'table_prefix' => $convertor_data['table_prefix'], + 'author' => $convertor_data['author'] + ); + } + unset($convertor_data); + } + } + closedir($handle); + + @ksort($sort); + + foreach ($sort as $void => $index) + { + $this->template->assign_block_vars('convertors', array( + 'AUTHOR' => $convertors[$index]['author'], + 'SOFTWARE' => $convertors[$index]['forum_name'], + 'VERSION' => $convertors[$index]['version'], + + 'U_CONVERT' => $this->controller_helper->route('phpbb_convert_settings', array('convertor' => $convertors[$index]['tag'])), + )); + } + + return $this->controller_helper->render('installer_convert.html', 'SUB_INTRO', true); + } + + /** + * Renders an error form + * + * @param string $msg + * @param string|bool $desc + */ + public function render_error($msg, $desc = false) + { + if ($this->request->is_ajax()) + { + $this->iohandler->add_error_message($msg, $desc); + $this->iohandler->send_response(); + } + else + { + $this->template->assign_vars(array( + 'S_ERROR_BOX' => true, + 'ERROR_TITLE' => $this->language->lang($msg), + )); + + if ($desc) + { + $this->template->assign_var('ERROR_MSG', $this->language->lang($desc)); + } + } + } + + /** + * Redirects an AJAX request to a non-JS version + * + * @param string $url URL to redirect to + */ + public function redirect_to_html($url) + { + $this->iohandler->redirect($url); + $this->iohandler->send_response(); + } + + private function setup_navigation($stage) + { + $active = true; + + switch ($stage) + { + case 'finish': + $this->navigation_provider->set_nav_property( + array('convert', 0, 'finish'), + array( + 'selected' => $active, + 'completed' => false, + ) + ); + + $active = false; + + $this->navigation_provider->set_nav_property( + array('convert', 0, 'convert'), + array('completed' => true) + ); + // no break; + + case 'convert': + $this->navigation_provider->set_nav_property( + array('convert', 0, 'convert'), + array( + 'selected' => $active, + 'completed' => false, + ) + ); + + $active = false; + + $this->navigation_provider->set_nav_property( + array('convert', 0, 'settings'), + array('completed' => true) + ); + // no break; + + case 'settings': + $this->navigation_provider->set_nav_property( + array('convert', 0, 'settings'), + array( + 'selected' => $active, + 'completed' => false, + ) + ); + + $active = false; + + $this->navigation_provider->set_nav_property( + array('convert', 0, 'intro'), + array( + 'selected' => $active, + 'completed' => false, + ) + ); + // no break; + + case 'intro': + $this->navigation_provider->set_nav_property( + array('convert', 0, 'intro'), + array( + 'selected' => $active, + 'completed' => false, + ) + ); + break; + } + } +} diff --git a/phpBB/install/convert/convert.php b/phpBB/install/convert/convert.php new file mode 100644 index 0000000000..d12059fe29 --- /dev/null +++ b/phpBB/install/convert/convert.php @@ -0,0 +1,60 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\convert; + +/** + * Class holding all convertor-specific details. + * + * WARNING: This file did not meant to be present in a production environment, so moving this file to a location which + * is accessible after board installation might lead to security issues. + */ +class convert +{ + var $options = array(); + + var $convertor_tag = ''; + var $src_dbms = ''; + var $src_dbhost = ''; + var $src_dbport = ''; + var $src_dbuser = ''; + var $src_dbpasswd = ''; + var $src_dbname = ''; + var $src_table_prefix = ''; + + var $convertor_data = array(); + var $tables = array(); + var $config_schema = array(); + var $convertor = array(); + var $src_truncate_statement = 'DELETE FROM '; + var $truncate_statement = 'DELETE FROM '; + + var $fulltext_search; + + // Batch size, can be adjusted by the conversion file + // For big boards a value of 6000 seems to be optimal + var $batch_size = 2000; + // Number of rows to be inserted at once (extended insert) if supported + // For installations having enough memory a value of 60 may be good. + var $num_wait_rows = 20; + + // Mysqls internal recoding engine messing up with our (better) functions? We at least support more encodings than mysql so should use it in favor. + var $mysql_convert = false; + + var $p_master; + + function convert(&$p_master) + { + $this->p_master = &$p_master; + } +} diff --git a/phpBB/install/convert/convertor.php b/phpBB/install/convert/convertor.php new file mode 100644 index 0000000000..8dbc38f303 --- /dev/null +++ b/phpBB/install/convert/convertor.php @@ -0,0 +1,1616 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\convert; + +use phpbb\install\controller\helper; +use phpbb\template\template; + +/** + * Convertor backend class + * + * WARNING: This file did not meant to be present in a production environment, so moving this file to a location which + * is accessible after board installation might lead to security issues. + */ +class convertor +{ + /** + * @var helper + */ + protected $controller_helper; + + /** + * @var \phpbb\filesystem\filesystem + */ + protected $filesystem; + + /** + * @var \phpbb\template\template + */ + protected $template; + + /** + * Constructor + * + * @param template $template + * @param helper $controller_helper + */ + public function __construct(template $template, helper $controller_helper) + { + global $convert, $phpbb_filesystem; + + $this->template = $template; + $this->filesystem = $phpbb_filesystem; + $this->controller_helper = $controller_helper; + + $convert = new convert($this); + } + + /** + * The function which does the actual work (or dispatches it to the relevant places) + */ + function convert_data($converter) + { + global $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache, $auth; + global $convert, $convert_row, $message_parser, $skip_rows, $language; + global $request, $phpbb_dispatcher; + + $phpbb_config_php_file = new \phpbb\config_php_file($phpbb_root_path, $phpEx); + extract($phpbb_config_php_file->get_all()); + + require_once($phpbb_root_path . 'includes/constants.' . $phpEx); + require_once($phpbb_root_path . 'includes/functions_convert.' . $phpEx); + + $dbms = $phpbb_config_php_file->convert_30_dbms_to_31($dbms); + + /** @var \phpbb\db\driver\driver_interface $db */ + $db = new $dbms(); + $db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, true); + unset($dbpasswd); + + // We need to fill the config to let internal functions correctly work + $config = new \phpbb\config\db($db, new \phpbb\cache\driver\dummy, CONFIG_TABLE); + + // Override a couple of config variables for the duration + $config['max_quote_depth'] = 0; + + // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues + $config['max_post_chars'] = $config['min_post_chars'] = 0; + + // Set up a user as well. We _should_ have enough of a database here at this point to do this + // and it helps for any core code we call + $user->session_begin(); + $user->page = $user->extract_current_page($phpbb_root_path); + + $convert->options = array(); + if (isset($config['convert_progress'])) + { + $convert->options = unserialize($config['convert_progress']); + $convert->options = array_merge($convert->options, unserialize($config['convert_db_server']), unserialize($config['convert_db_user']), unserialize($config['convert_options'])); + } + + // This information should have already been checked once, but do it again for safety + if (empty($convert->options) || empty($convert->options['tag']) || + !isset($convert->options['dbms']) || + !isset($convert->options['dbhost']) || + !isset($convert->options['dbport']) || + !isset($convert->options['dbuser']) || + !isset($convert->options['dbpasswd']) || + !isset($convert->options['dbname']) || + !isset($convert->options['table_prefix'])) + { + $this->error($user->lang['NO_CONVERT_SPECIFIED'], __LINE__, __FILE__); + } + + $this->template->assign_var('S_CONV_IN_PROGRESS', true); + + // Make some short variables accessible, for easier referencing + $convert->convertor_tag = basename($convert->options['tag']); + $convert->src_dbms = $convert->options['dbms']; + $convert->src_dbhost = $convert->options['dbhost']; + $convert->src_dbport = $convert->options['dbport']; + $convert->src_dbuser = $convert->options['dbuser']; + $convert->src_dbpasswd = $convert->options['dbpasswd']; + $convert->src_dbname = $convert->options['dbname']; + $convert->src_table_prefix = $convert->options['table_prefix']; + + // initiate database connection to old db if old and new db differ + global $src_db, $same_db; + $src_db = $same_db = null; + if ($convert->src_dbms != $dbms || $convert->src_dbhost != $dbhost || $convert->src_dbport != $dbport || $convert->src_dbname != $dbname || $convert->src_dbuser != $dbuser) + { + $dbms = $convert->src_dbms; + /** @var \phpbb\db\driver\driver $src_db */ + $src_db = new $dbms(); + $src_db->sql_connect($convert->src_dbhost, $convert->src_dbuser, htmlspecialchars_decode($convert->src_dbpasswd), $convert->src_dbname, $convert->src_dbport, false, true); + $same_db = false; + } + else + { + $src_db = $db; + $same_db = true; + } + + $convert->mysql_convert = false; + switch ($src_db->sql_layer) + { + case 'sqlite': + case 'sqlite3': + $convert->src_truncate_statement = 'DELETE FROM '; + break; + + // Thanks MySQL, for silently converting... + case 'mysql': + case 'mysql4': + if (version_compare($src_db->sql_server_info(true, false), '4.1.3', '>=')) + { + $convert->mysql_convert = true; + } + $convert->src_truncate_statement = 'TRUNCATE TABLE '; + break; + + case 'mysqli': + $convert->mysql_convert = true; + $convert->src_truncate_statement = 'TRUNCATE TABLE '; + break; + + default: + $convert->src_truncate_statement = 'TRUNCATE TABLE '; + break; + } + + if ($convert->mysql_convert && !$same_db) + { + $src_db->sql_query("SET NAMES 'binary'"); + } + + switch ($db->get_sql_layer()) + { + case 'sqlite': + case 'sqlite3': + $convert->truncate_statement = 'DELETE FROM '; + break; + + default: + $convert->truncate_statement = 'TRUNCATE TABLE '; + break; + } + + $get_info = false; + + // check security implications of direct inclusion + if (!file_exists('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx)) + { + $this->error($user->lang['CONVERT_NOT_EXIST'], __LINE__, __FILE__); + } + + if (file_exists('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx)) + { + include_once('./convertors/functions_' . $convert->convertor_tag . '.' . $phpEx); + } + + $get_info = true; + include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx); + + // Map some variables... + $convert->convertor_data = $convertor_data; + $convert->tables = $tables; + $convert->config_schema = $config_schema; + + // Now include the real data + $get_info = false; + include('./convertors/convert_' . $convert->convertor_tag . '.' . $phpEx); + + $convert->convertor_data = $convertor_data; + $convert->tables = $tables; + $convert->config_schema = $config_schema; + $convert->convertor = $convertor; + + // The test_file is a file that should be present in the location of the old board. + if (!file_exists($convert->options['forum_path'] . '/' . $test_file)) + { + $this->error(sprintf($user->lang['COULD_NOT_FIND_PATH'], $convert->options['forum_path']), __LINE__, __FILE__); + } + + $search_type = $config['search_type']; + + // For conversions we are a bit less strict and set to a search backend we know exist... + if (!class_exists($search_type)) + { + $search_type = '\phpbb\search\fulltext_native'; + $config->set('search_type', $search_type); + } + + if (!class_exists($search_type)) + { + trigger_error('NO_SUCH_SEARCH_MODULE'); + } + + $error = false; + $convert->fulltext_search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher); + + if ($error) + { + trigger_error($error); + } + + include_once($phpbb_root_path . 'includes/message_parser.' . $phpEx); + $message_parser = new \parse_message(); + + $jump = $request->variable('jump', 0); + $final_jump = $request->variable('final_jump', 0); + $sync_batch = $request->variable('sync_batch', -1); + $last_statement = $request->variable('last', 0); + + // We are running sync... + if ($sync_batch >= 0) + { + $this->sync_forums($converter, $sync_batch); + return; + } + + if ($jump) + { + $this->jump($converter, $jump, $last_statement); + return; + } + + if ($final_jump) + { + $this->final_jump($final_jump); + return; + } + + $current_table = $request->variable('current_table', 0); + $old_current_table = min(-1, $current_table - 1); + $skip_rows = $request->variable('skip_rows', 0); + + if (!$current_table && !$skip_rows) + { + if (!$request->variable('confirm', false)) + { + // If avatars / ranks / smilies folders are specified make sure they are writable + $bad_folders = array(); + + $local_paths = array( + 'avatar_path' => path($config['avatar_path']), + 'avatar_gallery_path' => path($config['avatar_gallery_path']), + 'icons_path' => path($config['icons_path']), + 'ranks_path' => path($config['ranks_path']), + 'smilies_path' => path($config['smilies_path']) + ); + + foreach ($local_paths as $folder => $local_path) + { + if (isset($convert->convertor[$folder])) + { + if (empty($convert->convertor['test_file'])) + { + // test_file is mandantory at the moment so this should never be reached, but just in case... + $this->error($user->lang['DEV_NO_TEST_FILE'], __LINE__, __FILE__); + } + + if (!$local_path || !$this->filesystem->is_writable($phpbb_root_path . $local_path)) + { + if (!$local_path) + { + $bad_folders[] = sprintf($user->lang['CONFIG_PHPBB_EMPTY'], $folder); + } + else + { + $bad_folders[] = $local_path; + } + } + } + } + + if (sizeof($bad_folders)) + { + $msg = (sizeof($bad_folders) == 1) ? $user->lang['MAKE_FOLDER_WRITABLE'] : $user->lang['MAKE_FOLDERS_WRITABLE']; + sort($bad_folders); + $this->error(sprintf($msg, implode('<br />', $bad_folders)), __LINE__, __FILE__, true); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['INSTALL_TEST'], + 'U_ACTION' => $this->controller_helper->route('phpbb_convert_convert', array('converter' => $converter)), + )); + return; + } + + // Grab all the tables used in convertor + $missing_tables = $tables_list = $aliases = array(); + + foreach ($convert->convertor['schema'] as $schema) + { + // Skip those not used (because of addons/plugins not detected) + if (!$schema['target']) + { + continue; + } + + foreach ($schema as $key => $val) + { + // we're dealing with an array like: + // array('forum_status', 'forums.forum_status', 'is_item_locked') + if (is_int($key) && !empty($val[1])) + { + $temp_data = $val[1]; + if (!is_array($temp_data)) + { + $temp_data = array($temp_data); + } + + foreach ($temp_data as $value) + { + if (preg_match('/([a-z0-9_]+)\.([a-z0-9_]+)\)* ?A?S? ?([a-z0-9_]*?)\.?([a-z0-9_]*)$/i', $value, $m)) + { + $table = $convert->src_table_prefix . $m[1]; + $tables_list[$table] = $table; + + if (!empty($m[3])) + { + $aliases[] = $convert->src_table_prefix . $m[3]; + } + } + } + } + // 'left_join' => 'topics LEFT JOIN vote_desc ON topics.topic_id = vote_desc.topic_id AND topics.topic_vote = 1' + else if ($key == 'left_join') + { + // Convert the value if it wasn't an array already. + if (!is_array($val)) + { + $val = array($val); + } + + for ($j = 0; $j < sizeof($val); ++$j) + { + if (preg_match('/LEFT JOIN ([a-z0-9_]+) AS ([a-z0-9_]+)/i', $val[$j], $m)) + { + $table = $convert->src_table_prefix . $m[1]; + $tables_list[$table] = $table; + + if (!empty($m[2])) + { + $aliases[] = $convert->src_table_prefix . $m[2]; + } + } + } + } + } + } + + // Remove aliased tables from $tables_list + foreach ($aliases as $alias) + { + unset($tables_list[$alias]); + } + + // Check if the tables that we need exist + $src_db->sql_return_on_error(true); + foreach ($tables_list as $table => $null) + { + $sql = 'SELECT 1 FROM ' . $table; + $_result = $src_db->sql_query_limit($sql, 1); + + if (!$_result) + { + $missing_tables[] = $table; + } + $src_db->sql_freeresult($_result); + } + $src_db->sql_return_on_error(false); + + // Throw an error if some tables are missing + // 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 + + if (sizeof($missing_tables) == sizeof($tables_list)) + { + $this->error($user->lang['NO_TABLES_FOUND'] . ' ' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__); + } + else if (sizeof($missing_tables)) + { + $this->error(sprintf($user->lang['TABLES_MISSING'], implode($user->lang['COMMA_SEPARATOR'], $missing_tables)) . '<br /><br />' . $user->lang['CHECK_TABLE_PREFIX'], __LINE__, __FILE__); + } + + $url = $this->save_convert_progress($converter, 'confirm=1'); + $msg = $user->lang['PRE_CONVERT_COMPLETE']; + + if ($convert->convertor_data['author_notes']) + { + $msg .= '</p><p>' . sprintf($user->lang['AUTHOR_NOTES'], $convert->convertor_data['author_notes']); + } + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'BODY' => $msg, + 'U_ACTION' => $url, + )); + + return; + } // if (!$request->variable('confirm', false))) + + $this->template->assign_block_vars('checks', array( + 'S_LEGEND' => true, + 'LEGEND' => $user->lang['STARTING_CONVERT'], + )); + + // Convert the config table and load the settings of the old board + if (!empty($convert->config_schema)) + { + restore_config($convert->config_schema); + + // Override a couple of config variables for the duration + $config['max_quote_depth'] = 0; + + // @todo Need to confirm that max post length in source is <= max post length in destination or there may be interesting formatting issues + $config['max_post_chars'] = $config['min_post_chars'] = 0; + } + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $user->lang['CONFIG_CONVERT'], + 'RESULT' => $user->lang['DONE'], + )); + + // Now process queries and execute functions that have to be executed prior to the conversion + if (!empty($convert->convertor['execute_first'])) + { + // @codingStandardsIgnoreStart + eval($convert->convertor['execute_first']); + // @codingStandardsIgnoreEnd + } + + if (!empty($convert->convertor['query_first'])) + { + if (!is_array($convert->convertor['query_first'])) + { + $convert->convertor['query_first'] = array('target', array($convert->convertor['query_first'])); + } + else if (!is_array($convert->convertor['query_first'][0])) + { + $convert->convertor['query_first'] = array(array($convert->convertor['query_first'][0], $convert->convertor['query_first'][1])); + } + + foreach ($convert->convertor['query_first'] as $query_first) + { + if ($query_first[0] == 'src') + { + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'binary'"); + } + + $src_db->sql_query($query_first[1]); + + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'utf8'"); + } + } + else + { + $db->sql_query($query_first[1]); + } + } + } + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $user->lang['PREPROCESS_STEP'], + 'RESULT' => $user->lang['DONE'], + )); + } // if (!$current_table && !$skip_rows) + + $this->template->assign_block_vars('checks', array( + 'S_LEGEND' => true, + 'LEGEND' => $user->lang['FILLING_TABLES'], + )); + + // This loop takes one target table and processes it + while ($current_table < sizeof($convert->convertor['schema'])) + { + $schema = $convert->convertor['schema'][$current_table]; + + // The target table isn't set, this can be because a module (for example the attachement mod) is taking care of this. + if (empty($schema['target'])) + { + $current_table++; + continue; + } + + $this->template->assign_block_vars('checks', array( + 'TITLE' => sprintf($user->lang['FILLING_TABLE'], $schema['target']), + )); + + // This is only the case when we first start working on the tables. + if (!$skip_rows) + { + // process execute_first and query_first for this table... + if (!empty($schema['execute_first'])) + { + // @codingStandardsIgnoreStart + eval($schema['execute_first']); + // @codingStandardsIgnoreEnd + } + + if (!empty($schema['query_first'])) + { + if (!is_array($schema['query_first'])) + { + $schema['query_first'] = array('target', array($schema['query_first'])); + } + else if (!is_array($schema['query_first'][0])) + { + $schema['query_first'] = array(array($schema['query_first'][0], $schema['query_first'][1])); + } + + foreach ($schema['query_first'] as $query_first) + { + if ($query_first[0] == 'src') + { + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'binary'"); + } + $src_db->sql_query($query_first[1]); + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'utf8'"); + } + } + else + { + $db->sql_query($query_first[1]); + } + } + } + + if (!empty($schema['autoincrement'])) + { + switch ($db->get_sql_layer()) + { + case 'postgres': + $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));'); + break; + + case 'oracle': + $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + $largest_id = (int) $row['max_id']; + + if ($largest_id) + { + $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq'); + $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1)); + } + break; + } + } + } + + // Process execute_always for this table + // This is for code which needs to be executed on every pass of this table if + // it gets split because of time restrictions + if (!empty($schema['execute_always'])) + { + // @codingStandardsIgnoreStart + eval($schema['execute_always']); + // @codingStandardsIgnoreEnd + } + + // + // Set up some variables + // + // $waiting_rows holds rows for multirows insertion (MySQL only) + // $src_tables holds unique tables with aliases to select from + // $src_fields will quickly refer source fields (or aliases) corresponding to the current index + // $select_fields holds the names of the fields to retrieve + // + + $sql_data = array( + 'source_fields' => array(), + 'target_fields' => array(), + 'source_tables' => array(), + 'select_fields' => array(), + ); + + // This statement is building the keys for later insertion. + $insert_query = $this->build_insert_query($schema, $sql_data, $current_table); + + // If no source table is affected, we skip the table + if (empty($sql_data['source_tables'])) + { + $skip_rows = 0; + $current_table++; + continue; + } + + $distinct = (!empty($schema['distinct'])) ? 'DISTINCT ' : ''; + + $sql = 'SELECT ' . $distinct . implode(', ', $sql_data['select_fields']) . " \nFROM " . implode(', ', $sql_data['source_tables']); + + // Where + $sql .= (!empty($schema['where'])) ? "\nWHERE (" . $schema['where'] . ')' : ''; + + // Group By + if (!empty($schema['group_by'])) + { + $schema['group_by'] = array($schema['group_by']); + foreach ($sql_data['select_fields'] as $select) + { + $alias = strpos(strtolower($select), ' as '); + $select = ($alias) ? substr($select, 0, $alias) : $select; + if (!in_array($select, $schema['group_by'])) + { + $schema['group_by'][] = $select; + } + } + } + $sql .= (!empty($schema['group_by'])) ? "\nGROUP BY " . implode(', ', $schema['group_by']) : ''; + + // Having + $sql .= (!empty($schema['having'])) ? "\nHAVING " . $schema['having'] : ''; + + // Order By + if (empty($schema['order_by']) && !empty($schema['primary'])) + { + $schema['order_by'] = $schema['primary']; + } + $sql .= (!empty($schema['order_by'])) ? "\nORDER BY " . $schema['order_by'] : ''; + + // Counting basically holds the amount of rows processed. + $counting = -1; + $batch_time = 0; + + while ($counting === -1 || ($counting >= $convert->batch_size && still_on_time())) + { + $old_current_table = $current_table; + + $rows = ''; + $waiting_rows = array(); + + if (!empty($batch_time)) + { + $mtime = explode(' ', microtime()); + $mtime = $mtime[0] + $mtime[1]; + $rows = ceil($counting/($mtime - $batch_time)) . " rows/s ($counting rows) | "; + } + + $this->template->assign_block_vars('checks', array( + 'TITLE' => "skip_rows = $skip_rows", + 'RESULT' => $rows . ((defined('DEBUG') && function_exists('memory_get_usage')) ? ceil(memory_get_usage()/1024) . ' ' . $user->lang['KIB'] : ''), + )); + + $mtime = explode(' ', microtime()); + $batch_time = $mtime[0] + $mtime[1]; + + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'binary'"); + } + + // Take skip rows into account and only fetch batch_size amount of rows + $___result = $src_db->sql_query_limit($sql, $convert->batch_size, $skip_rows); + + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'utf8'"); + } + + // This loop processes each row + $counting = 0; + + $convert->row = $convert_row = array(); + + if (!empty($schema['autoincrement'])) + { + switch ($db->get_sql_layer()) + { + case 'mssql': + case 'mssql_odbc': + case 'mssqlnative': + $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' ON'); + break; + } + } + + // Now handle the rows until time is over or no more rows to process... + while ($counting === 0 || still_on_time()) + { + $convert_row = $src_db->sql_fetchrow($___result); + + if (!$convert_row) + { + // move to the next batch or table + break; + } + + // With this we are able to always save the last state + $convert->row = $convert_row; + + // Increment the counting variable, it stores the number of rows we have processed + $counting++; + + $insert_values = array(); + + $sql_flag = $this->process_row($schema, $sql_data, $insert_values); + + if ($sql_flag === true) + { + switch ($db->get_sql_layer()) + { + // If MySQL, we'll wait to have num_wait_rows rows to submit at once + case 'mysql': + case 'mysql4': + case 'mysqli': + $waiting_rows[] = '(' . implode(', ', $insert_values) . ')'; + + if (sizeof($waiting_rows) >= $convert->num_wait_rows) + { + $errored = false; + + $db->sql_return_on_error(true); + + if (!$db->sql_query($insert_query . implode(', ', $waiting_rows))) + { + $errored = true; + } + $db->sql_return_on_error(false); + + if ($errored) + { + $db->sql_return_on_error(true); + + // Because it errored out we will try to insert the rows one by one... most of the time this + // is caused by duplicate entries - but we also do not want to miss one... + foreach ($waiting_rows as $waiting_sql) + { + if (!$db->sql_query($insert_query . $waiting_sql)) + { + $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true); + } + } + + $db->sql_return_on_error(false); + } + + $waiting_rows = array(); + } + + break; + + default: + $insert_sql = $insert_query . '(' . implode(', ', $insert_values) . ')'; + + $db->sql_return_on_error(true); + + if (!$db->sql_query($insert_sql)) + { + $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true); + } + $db->sql_return_on_error(false); + + $waiting_rows = array(); + + break; + } + } + + $skip_rows++; + } + $src_db->sql_freeresult($___result); + + // We might still have some rows waiting + if (sizeof($waiting_rows)) + { + $errored = false; + $db->sql_return_on_error(true); + + if (!$db->sql_query($insert_query . implode(', ', $waiting_rows))) + { + $errored = true; + } + $db->sql_return_on_error(false); + + if ($errored) + { + $db->sql_return_on_error(true); + + // Because it errored out we will try to insert the rows one by one... most of the time this + // is caused by duplicate entries - but we also do not want to miss one... + foreach ($waiting_rows as $waiting_sql) + { + $db->sql_query($insert_query . $waiting_sql); + $this->db_error($user->lang['DB_ERR_INSERT'], htmlspecialchars($insert_query . $waiting_sql) . '<br /><br />' . htmlspecialchars(print_r($db->_sql_error(), true)), __LINE__, __FILE__, true); + } + + $db->sql_return_on_error(false); + } + + $waiting_rows = array(); + } + + if (!empty($schema['autoincrement'])) + { + switch ($db->get_sql_layer()) + { + case 'mssql': + case 'mssql_odbc': + case 'mssqlnative': + $db->sql_query('SET IDENTITY_INSERT ' . $schema['target'] . ' OFF'); + break; + + case 'postgres': + $db->sql_query("SELECT SETVAL('" . $schema['target'] . "_seq',(select case when max(" . $schema['autoincrement'] . ")>0 then max(" . $schema['autoincrement'] . ")+1 else 1 end from " . $schema['target'] . '));'); + break; + + case 'oracle': + $result = $db->sql_query('SELECT MAX(' . $schema['autoincrement'] . ') as max_id FROM ' . $schema['target']); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + $largest_id = (int) $row['max_id']; + + if ($largest_id) + { + $db->sql_query('DROP SEQUENCE ' . $schema['target'] . '_seq'); + $db->sql_query('CREATE SEQUENCE ' . $schema['target'] . '_seq START WITH ' . ($largest_id + 1)); + } + break; + } + } + } + + // When we reach this point, either the current table has been processed or we're running out of time. + if (still_on_time() && $counting < $convert->batch_size/* && !defined('DEBUG')*/) + { + $skip_rows = 0; + $current_table++; + } + else + {/* + if (still_on_time() && $counting < $convert->batch_size) + { + $skip_rows = 0; + $current_table++; + }*/ + + // Looks like we ran out of time. + $url = $this->save_convert_progress($converter, 'current_table=' . $current_table . '&skip_rows=' . $skip_rows); + + $current_table++; +// $percentage = ($skip_rows == 0) ? 0 : floor(100 / ($total_rows / $skip_rows)); + + $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $current_table, sizeof($convert->convertor['schema'])); + + $this->template->assign_vars(array( + 'BODY' => $msg, + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + } + + // Process execute_last then we'll be done + $url = $this->save_convert_progress($converter, 'jump=1'); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['FINAL_STEP'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + + /** + * Sync function being executed at the middle, some functions need to be executed after a successful sync. + */ + function sync_forums($converter, $sync_batch) + { + global $user, $db, $phpbb_root_path, $phpEx, $config, $cache; + global $convert; + + include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx); + + $this->template->assign_block_vars('checks', array( + 'S_LEGEND' => true, + 'LEGEND' => $user->lang['SYNC_TOPICS'], + )); + + $batch_size = $convert->batch_size; + + $sql = 'SELECT MIN(topic_id) as min_value, MAX(topic_id) AS max_value + FROM ' . TOPICS_TABLE; + $result = $db->sql_query($sql); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + // Set values of minimum/maximum primary value for this table. + $primary_min = $row['min_value']; + $primary_max = $row['max_value']; + + if ($sync_batch == 0) + { + $sync_batch = (int) $primary_min; + } + + if ($sync_batch == 0) + { + $sync_batch = 1; + } + + // Fetch a batch of rows, process and insert them. + while ($sync_batch <= $primary_max && still_on_time()) + { + $end = ($sync_batch + $batch_size - 1); + + // Sync all topics in batch mode... + sync('topic', 'range', 'topic_id BETWEEN ' . $sync_batch . ' AND ' . $end, true, true); + + $this->template->assign_block_vars('checks', array( + 'TITLE' => sprintf($user->lang['SYNC_TOPIC_ID'], $sync_batch, ($sync_batch + $batch_size)) . ((defined('DEBUG') && function_exists('memory_get_usage')) ? ' [' . ceil(memory_get_usage()/1024) . ' ' . $user->lang['KIB'] . ']' : ''), + 'RESULT' => $user->lang['DONE'], + )); + + $sync_batch += $batch_size; + } + + if ($sync_batch >= $primary_max) + { + $url = $this->save_convert_progress($converter, 'final_jump=1'); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + else + { + $sync_batch--; + } + + $url = $this->save_convert_progress($converter, 'sync_batch=' . $sync_batch); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + + /** + * Save the convertor status + */ + function save_convert_progress($convertor_tag, $step) + { + global $config, $convert, $language; + + // Save convertor Status + $config->set('convert_progress', serialize(array( + 'step' => $step, + 'table_prefix' => $convert->src_table_prefix, + 'tag' => $convert->convertor_tag, + )), false); + + $config->set('convert_db_server', serialize(array( + 'dbms' => $convert->src_dbms, + 'dbhost' => $convert->src_dbhost, + 'dbport' => $convert->src_dbport, + 'dbname' => $convert->src_dbname, + )), false); + + $config->set('convert_db_user', serialize(array( + 'dbuser' => $convert->src_dbuser, + 'dbpasswd' => $convert->src_dbpasswd, + )), false); + + return $this->controller_helper->route('phpbb_convert_convert', array('converter' => $convertor_tag)) . '?' . $step; + } + + /** + * Finish conversion, the last function to be called. + */ + function finish_conversion() + { + global $db, $phpbb_root_path, $phpEx, $convert, $config, $language, $user; + global $cache, $auth, $phpbb_container, $phpbb_log; + + include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx); + + $db->sql_query('DELETE FROM ' . CONFIG_TABLE . " + WHERE config_name = 'convert_progress' + OR config_name = 'convert_options' + OR config_name = 'convert_db_server' + OR config_name = 'convert_db_user'"); + $db->sql_query('DELETE FROM ' . SESSIONS_TABLE); + + @unlink($phpbb_root_path . 'cache/data_global.' . $phpEx); + phpbb_cache_moderators($db, $cache, $auth); + + // And finally, add a note to the log + $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_INSTALL_CONVERTED', false, array($convert->convertor_data['forum_name'], $config['version'])); + + $url = $this->controller_helper->route('phpbb_convert_finish'); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['FINAL_STEP'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + + /** + * This function marks the steps after syncing + */ + function final_jump($final_jump) + { + global $user, $src_db, $same_db, $db, $phpbb_root_path, $phpEx, $config, $cache; + global $convert; + + $this->template->assign_block_vars('checks', array( + 'S_LEGEND' => true, + 'LEGEND' => $user->lang['PROCESS_LAST'], + )); + + if ($final_jump == 1) + { + $db->sql_return_on_error(true); + + update_topics_posted(); + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $user->lang['UPDATE_TOPICS_POSTED'], + 'RESULT' => $user->lang['DONE'], + )); + + if ($db->get_sql_error_triggered()) + { + $this->template->assign_vars(array( + 'S_ERROR_BOX' => true, + 'ERROR_TITLE' => $user->lang['UPDATE_TOPICS_POSTED'], + 'ERROR_MSG' => $user->lang['UPDATE_TOPICS_POSTED_ERR'], + )); + } + $db->sql_return_on_error(false); + + $this->finish_conversion(); + return; + } + } + + /** + * This function marks the steps before syncing (jump=1) + */ + function jump($converter, $jump, $last_statement) + { + /** @var \phpbb\db\driver\driver_interface $src_db */ + /** @var \phpbb\cache\driver\driver_interface $cache */ + global $user, $src_db, $same_db, $db, $phpbb_root_path, $phpEx, $config, $cache; + global $convert; + + include_once ($phpbb_root_path . 'includes/functions_admin.' . $phpEx); + + $this->template->assign_block_vars('checks', array( + 'S_LEGEND' => true, + 'LEGEND' => $user->lang['PROCESS_LAST'], + )); + + if ($jump == 1) + { + // Execute 'last' statements/queries + if (!empty($convert->convertor['execute_last'])) + { + if (!is_array($convert->convertor['execute_last'])) + { + // @codingStandardsIgnoreStart + eval($convert->convertor['execute_last']); + // @codingStandardsIgnoreEnd + } + else + { + while ($last_statement < sizeof($convert->convertor['execute_last'])) + { + // @codingStandardsIgnoreStart + eval($convert->convertor['execute_last'][$last_statement]); + // @codingStandardsIgnoreEnd + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $convert->convertor['execute_last'][$last_statement], + 'RESULT' => $user->lang['DONE'], + )); + + $last_statement++; + $url = $this->save_convert_progress($converter, 'jump=1&last=' . $last_statement); + + $percentage = ($last_statement == 0) ? 0 : floor(100 / (sizeof($convert->convertor['execute_last']) / $last_statement)); + $msg = sprintf($user->lang['STEP_PERCENT_COMPLETED'], $last_statement, sizeof($convert->convertor['execute_last']), $percentage); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_LAST'], + 'BODY' => $msg, + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + } + } + + if (!empty($convert->convertor['query_last'])) + { + if (!is_array($convert->convertor['query_last'])) + { + $convert->convertor['query_last'] = array('target', array($convert->convertor['query_last'])); + } + else if (!is_array($convert->convertor['query_last'][0])) + { + $convert->convertor['query_last'] = array(array($convert->convertor['query_last'][0], $convert->convertor['query_last'][1])); + } + + foreach ($convert->convertor['query_last'] as $query_last) + { + if ($query_last[0] == 'src') + { + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'binary'"); + } + + $src_db->sql_query($query_last[1]); + + if ($convert->mysql_convert && $same_db) + { + $src_db->sql_query("SET NAMES 'utf8'"); + } + } + else + { + $db->sql_query($query_last[1]); + } + } + } + + // Sanity check + $db->sql_return_on_error(false); + $src_db->sql_return_on_error(false); + + fix_empty_primary_groups(); + + $sql = 'SELECT MIN(user_regdate) AS board_startdate + FROM ' . USERS_TABLE; + $result = $db->sql_query($sql); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + if (!isset($config['board_startdate']) || ($row['board_startdate'] < $config['board_startdate'] && $row['board_startdate'] > 0)) + { + $config->set('board_startdate', $row['board_startdate']); + $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_regdate = ' . $row['board_startdate'] . ' WHERE user_id = ' . ANONYMOUS); + } + + update_dynamic_config(); + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $user->lang['CLEAN_VERIFY'], + 'RESULT' => $user->lang['DONE'], + )); + + $url = $this->save_convert_progress($converter, 'jump=2'); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + + if ($jump == 2) + { + $db->sql_query('UPDATE ' . USERS_TABLE . " SET user_permissions = ''"); + + // TODO: sync() is likely going to bomb out on forums with a considerable amount of topics. + // TODO: the sync function is able to handle FROM-TO values, we should use them here (batch processing) + sync('forum', '', '', false, true); + $cache->destroy('sql', FORUMS_TABLE); + + $this->template->assign_block_vars('checks', array( + 'TITLE' => $user->lang['SYNC_FORUMS'], + 'RESULT' => $user->lang['DONE'], + )); + + // Continue with synchronizing the forums... + $url = $this->save_convert_progress($converter, 'sync_batch=0'); + + $this->template->assign_vars(array( + 'L_SUBMIT' => $user->lang['CONTINUE_CONVERT'], + 'U_ACTION' => $url, + )); + + $this->meta_refresh($url); + return; + } + } + + function build_insert_query(&$schema, &$sql_data, $current_table) + { + global $db, $user; + global $convert; + + // Can we use IGNORE with this DBMS? + $sql_ignore = (strpos($db->get_sql_layer(), 'mysql') === 0 && !defined('DEBUG')) ? 'IGNORE ' : ''; + $insert_query = 'INSERT ' . $sql_ignore . 'INTO ' . $schema['target'] . ' ('; + + $aliases = array(); + + $sql_data = array( + 'source_fields' => array(), + 'target_fields' => array(), + 'source_tables' => array(), + 'select_fields' => array(), + ); + + foreach ($schema as $key => $val) + { + // Example: array('group_name', 'extension_groups.group_name', 'htmlspecialchars'), + if (is_int($key)) + { + if (!empty($val[0])) + { + // Target fields + $sql_data['target_fields'][$val[0]] = $key; + $insert_query .= $val[0] . ', '; + } + + if (!is_array($val[1])) + { + $val[1] = array($val[1]); + } + + foreach ($val[1] as $valkey => $value_1) + { + // This should cover about any case: + // + // table.field => SELECT table.field FROM table + // table.field AS alias => SELECT table.field AS alias FROM table + // table.field AS table2.alias => SELECT table2.field AS alias FROM table table2 + // table.field AS table2.field => SELECT table2.field FROM table table2 + // + if (preg_match('/^([a-z0-9_]+)\.([a-z0-9_]+)( +AS +(([a-z0-9_]+?)\.)?([a-z0-9_]+))?$/i', $value_1, $m)) + { + // There is 'AS ...' in the field names + if (!empty($m[3])) + { + $value_1 = ($m[2] == $m[6]) ? $m[1] . '.' . $m[2] : $m[1] . '.' . $m[2] . ' AS ' . $m[6]; + + // Table alias: store it then replace the source table with it + if (!empty($m[5]) && $m[5] != $m[1]) + { + $aliases[$m[5]] = $m[1]; + $value_1 = str_replace($m[1] . '.' . $m[2], $m[5] . '.' . $m[2], $value_1); + } + } + else + { + // No table alias + $sql_data['source_tables'][$m[1]] = (empty($convert->src_table_prefix)) ? $m[1] : $convert->src_table_prefix . $m[1] . ' ' . $m[1]; + } + + $sql_data['select_fields'][$value_1] = $value_1; + $sql_data['source_fields'][$key][$valkey] = (!empty($m[6])) ? $m[6] : $m[2]; + } + } + } + else if ($key == 'where' || $key == 'group_by' || $key == 'order_by' || $key == 'having') + { + if (@preg_match_all('/([a-z0-9_]+)\.([a-z0-9_]+)/i', $val, $m)) + { + foreach ($m[1] as $value) + { + $sql_data['source_tables'][$value] = (empty($convert->src_table_prefix)) ? $value : $convert->src_table_prefix . $value . ' ' . $value; + } + } + } + } + + // Add the aliases to the list of tables + foreach ($aliases as $alias => $table) + { + $sql_data['source_tables'][$alias] = $convert->src_table_prefix . $table . ' ' . $alias; + } + + // 'left_join' => 'forums LEFT JOIN forum_prune ON forums.forum_id = forum_prune.forum_id', + if (!empty($schema['left_join'])) + { + if (!is_array($schema['left_join'])) + { + $schema['left_join'] = array($schema['left_join']); + } + + foreach ($schema['left_join'] as $left_join) + { + // This won't handle concatened LEFT JOINs + if (!preg_match('/([a-z0-9_]+) LEFT JOIN ([a-z0-9_]+) A?S? ?([a-z0-9_]*?) ?(ON|USING)(.*)/i', $left_join, $m)) + { + $this->error(sprintf($user->lang['NOT_UNDERSTAND'], 'LEFT JOIN', $left_join, $current_table, $schema['target']), __LINE__, __FILE__); + } + + if (!empty($aliases[$m[2]])) + { + if (!empty($m[3])) + { + $this->error(sprintf($user->lang['NAMING_CONFLICT'], $m[2], $m[3], $schema['left_join']), __LINE__, __FILE__); + } + + $m[2] = $aliases[$m[2]]; + $m[3] = $m[2]; + } + + $right_table = $convert->src_table_prefix . $m[2]; + if (!empty($m[3])) + { + unset($sql_data['source_tables'][$m[3]]); + } + else if ($m[2] != $m[1]) + { + unset($sql_data['source_tables'][$m[2]]); + } + + if (strpos($sql_data['source_tables'][$m[1]], "\nLEFT JOIN") !== false) + { + $sql_data['source_tables'][$m[1]] = '(' . $sql_data['source_tables'][$m[1]] . ")\nLEFT JOIN $right_table"; + } + else + { + $sql_data['source_tables'][$m[1]] .= "\nLEFT JOIN $right_table"; + } + + if (!empty($m[3])) + { + unset($sql_data['source_tables'][$m[3]]); + $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[3]; + } + else if (!empty($convert->src_table_prefix)) + { + $sql_data['source_tables'][$m[1]] .= ' AS ' . $m[2]; + } + $sql_data['source_tables'][$m[1]] .= ' ' . $m[4] . $m[5]; + } + } + + // Remove ", " from the end of the insert query + $insert_query = substr($insert_query, 0, -2) . ') VALUES '; + + return $insert_query; + } + + /** + * Function for processing the currently handled row + */ + function process_row(&$schema, &$sql_data, &$insert_values) + { + global $user, $phpbb_root_path, $phpEx, $db, $lang, $config, $cache; + global $convert, $convert_row; + + $sql_flag = false; + + foreach ($schema as $key => $fields) + { + // We are only interested in the lines with: + // array('comment', 'attachments_desc.comment', 'htmlspecialchars'), + if (is_int($key)) + { + if (!is_array($fields[1])) + { + $fields[1] = array($fields[1]); + } + + $firstkey_set = false; + $firstkey = 0; + + foreach ($fields[1] as $inner_key => $inner_value) + { + if (!$firstkey_set) + { + $firstkey = $inner_key; + $firstkey_set = true; + } + + $src_field = isset($sql_data['source_fields'][$key][$inner_key]) ? $sql_data['source_fields'][$key][$inner_key] : ''; + + if (!empty($src_field)) + { + $fields[1][$inner_key] = $convert->row[$src_field]; + } + } + + if (!empty($fields[0])) + { + // We have a target field, if we haven't set $sql_flag yet it will be set to TRUE. + // If a function has already set it to FALSE it won't change it. + if ($sql_flag === false) + { + $sql_flag = true; + } + + // No function assigned? + if (empty($fields[2])) + { + $value = $fields[1][$firstkey]; + } + else if (is_array($fields[2]) && !is_callable($fields[2])) + { + // Execute complex function/eval/typecast + $value = $fields[1]; + + foreach ($fields[2] as $type => $execution) + { + if (strpos($type, 'typecast') === 0) + { + if (!is_array($value)) + { + $value = array($value); + } + $value = $value[0]; + settype($value, $execution); + } + else if (strpos($type, 'function') === 0) + { + if (!is_array($value)) + { + $value = array($value); + } + + $value = call_user_func_array($execution, $value); + } + else if (strpos($type, 'execute') === 0) + { + if (!is_array($value)) + { + $value = array($value); + } + + $execution = str_replace('{RESULT}', '$value', $execution); + $execution = str_replace('{VALUE}', '$value', $execution); + // @codingStandardsIgnoreStart + eval($execution); + // @codingStandardsIgnoreEnd + } + } + } + else + { + $value = call_user_func_array($fields[2], $fields[1]); + } + + if (is_null($value)) + { + $value = ''; + } + + $insert_values[] = $db->_sql_validate_value($value); + } + else if (!empty($fields[2])) + { + if (is_array($fields[2])) + { + // Execute complex function/eval/typecast + $value = ''; + + foreach ($fields[2] as $type => $execution) + { + if (strpos($type, 'typecast') === 0) + { + $value = settype($value, $execution); + } + else if (strpos($type, 'function') === 0) + { + if (!is_array($value)) + { + $value = array($value); + } + + $value = call_user_func_array($execution, $value); + } + else if (strpos($type, 'execute') === 0) + { + if (!is_array($value)) + { + $value = array($value); + } + + $execution = str_replace('{RESULT}', '$value', $execution); + $execution = str_replace('{VALUE}', '$value', $execution); + // @codingStandardsIgnoreStart + eval($execution); + // @codingStandardsIgnoreEnd + } + } + } + else + { + call_user_func_array($fields[2], $fields[1]); + } + } + } + } + + return $sql_flag; + } + + /** + * Own meta refresh function to be able to change the global time used + */ + function meta_refresh($url) + { + global $convert; + + if ($convert->options['refresh']) + { + // Because we should not rely on correct settings, we simply use the relative path here directly. + $this->template->assign_vars(array( + 'S_REFRESH' => true, + 'META' => '<meta http-equiv="refresh" content="5; url=' . $url . '" />') + ); + } + } + + /** + * Error handler function + * + * This function needs to be kept for BC + * + * @param $error + * @param $line + * @param $file + * @param bool|false $skip + */ + public function error($error, $line, $file, $skip = false) + { + // + // @todo + // + } + + /** + * Database error handler function + * + * This function needs to be kept for BC + * + * @param $error + * @param $sql + * @param $line + * @param $file + * @param bool|false $skip + */ + public function db_error($error, $sql, $line, $file, $skip = false) + { + // + // @todo + // + } +} diff --git a/phpBB/install/startup.php b/phpBB/install/startup.php index 927f529b73..711768f9ac 100644 --- a/phpBB/install/startup.php +++ b/phpBB/install/startup.php @@ -101,6 +101,8 @@ $phpbb_class_loader_new = new \phpbb\class_loader('phpbb\\', "{$phpbb_root_path} $phpbb_class_loader_new->register(); $phpbb_class_loader = new \phpbb\class_loader('phpbb\\', "{$phpbb_root_path}phpbb/", $phpEx); $phpbb_class_loader->register(); +$phpbb_class_loader = new \phpbb\class_loader('phpbb\\convert\\', "{$phpbb_root_path}install/convert/", $phpEx); +$phpbb_class_loader->register(); $phpbb_class_loader_ext = new \phpbb\class_loader('\\', "{$phpbb_root_path}ext/", $phpEx); $phpbb_class_loader_ext->register(); diff --git a/phpBB/language/en/install.php b/phpBB/language/en/install.php index 0c539a7309..5b761dcb19 100644 --- a/phpBB/language/en/install.php +++ b/phpBB/language/en/install.php @@ -439,3 +439,112 @@ $lang = array_merge($lang, array( 'INLINE_UPDATE_SUCCESSFUL' => 'The database update was successful.', )); + +// Converter +$lang = array_merge($lang, array( + // Common converter messages + 'CONVERT_NOT_EXIST' => 'The specified convertor does not exist.', + 'DEV_NO_TEST_FILE' => 'No value has been specified for the test_file variable in the convertor. If you are a user of this convertor, you should not be seeing this error, please report this message to the convertor author. If you are a convertor author, you must specify the name of a file which exists in the source board to allow the path to it to be verified.', + 'COULD_NOT_FIND_PATH' => 'Could not find path to your former board. Please check your settings and try again.<br />» %s was specified as the source path.', + 'CONFIG_PHPBB_EMPTY' => 'The phpBB3 config variable for “%s” is empty.', + + 'MAKE_FOLDER_WRITABLE' => 'Please make sure that this folder exists and is writable by the webserver then try again:<br />»<strong>%s</strong>.', + 'MAKE_FOLDERS_WRITABLE' => 'Please make sure that these folders exist and are writable by the webserver then try again:<br />»<strong>%s</strong>.', + + 'INSTALL_TEST' => 'Test again', + + 'NO_TABLES_FOUND' => 'No tables found.', + 'TABLES_MISSING' => 'Could not find these tables<br />» <strong>%s</strong>.', + 'CHECK_TABLE_PREFIX' => 'Please check your table prefix and try again.', + + // Conversion in progress + 'CONTINUE_CONVERT' => 'Continue conversion', + 'CONTINUE_CONVERT_BODY' => 'A previous conversion attempt has been determined. You are now able to choose between starting a new conversion or continuing the conversion.', + 'CONVERT_NEW_CONVERSION' => 'New conversion', + 'CONTINUE_OLD_CONVERSION' => 'Continue previously started conversion', + + // Start conversion + 'SUB_INTRO' => 'Introduction', + 'CONVERT_INTRO' => 'Welcome to the phpBB Unified Convertor Framework', + 'CONVERT_INTRO_BODY' => 'From here, you are able to import data from other (installed) board systems. The list below shows all the conversion modules currently available. If there is no convertor shown in this list for the board software you wish to convert from, please check our website where further conversion modules may be available for download.', + 'AVAILABLE_CONVERTORS' => 'Available convertors', + 'NO_CONVERTORS' => 'No convertors are available for use.', + 'CONVERT_OPTIONS' => 'Options', + 'SOFTWARE' => 'Board software', + 'VERSION' => 'Version', + 'CONVERT' => 'Convert', + + // Settings + 'STAGE_SETTINGS' => 'Settings', + 'TABLE_PREFIX_SAME' => 'The table prefix needs to be the one used by the software you are converting from.<br />» Specified table prefix was %s.', + 'DEFAULT_PREFIX_IS' => 'The convertor was not able to find tables with the specified prefix. Please make sure you have entered the correct details for the board you are converting from. The default table prefix for %1$s is <strong>%2$s</strong>.', + 'SPECIFY_OPTIONS' => 'Specify conversion options', + 'FORUM_PATH' => 'Board path', + 'FORUM_PATH_EXPLAIN' => 'This is the <strong>relative</strong> path on disk to your former board from the <strong>root of this phpBB3 installation</strong>.', + 'REFRESH_PAGE' => 'Refresh page to continue conversion', + 'REFRESH_PAGE_EXPLAIN' => 'If set to yes, the convertor will refresh the page to continue the conversion after having finished a step. If this is your first conversion for testing purposes and to determine any errors in advance, we suggest to set this to No.', + + // Conversion + 'STAGE_IN_PROGRESS' => 'Conversion in progress', + + 'AUTHOR_NOTES' => 'Author notes<br />» %s', + 'STARTING_CONVERT' => 'Starting conversion process', + 'CONFIG_CONVERT' => 'Converting the configuration', + 'DONE' => 'Done', + 'PREPROCESS_STEP' => 'Executing pre-processing functions/queries', + 'FILLING_TABLE' => 'Filling table <strong>%s</strong>', + 'FILLING_TABLES' => 'Filling tables', + 'DB_ERR_INSERT' => 'Error while processing <code>INSERT</code> query.', + 'DB_ERR_LAST' => 'Error while processing <var>query_last</var>.', + 'DB_ERR_QUERY_FIRST' => 'Error while executing <var>query_first</var>.', + 'DB_ERR_QUERY_FIRST_TABLE' => 'Error while executing <var>query_first</var>, %s (“%s”).', + 'DB_ERR_SELECT' => 'Error while running <code>SELECT</code> query.', + 'STEP_PERCENT_COMPLETED' => 'Step <strong>%d</strong> of <strong>%d</strong>', + 'FINAL_STEP' => 'Process final step', + 'SYNC_FORUMS' => 'Starting to synchronise forums', + 'SYNC_POST_COUNT' => 'Synchronising post_counts', + 'SYNC_POST_COUNT_ID' => 'Synchronising post_counts from <var>entry</var> %1$s to %2$s.', + 'SYNC_TOPICS' => 'Starting to synchronise topics', + 'SYNC_TOPIC_ID' => 'Synchronising topics from <var>topic_id</var> %1$s to %2$s.', + 'PROCESS_LAST' => 'Processing last statements', + 'UPDATE_TOPICS_POSTED' => 'Generating topics posted information', + 'UPDATE_TOPICS_POSTED_ERR' => 'An error occurred while generating topics posted information. You can retry this step in the ACP after the conversion process is completed.', + 'CONTINUE_LAST' => 'Continue last statements', + 'CLEAN_VERIFY' => 'Cleaning up and verifying the final structure', + 'NOT_UNDERSTAND' => 'Could not understand %s #%d, table %s (“%s”)', + 'NAMING_CONFLICT' => 'Naming conflict: %s and %s are both aliases<br /><br />%s', + + // Finish conversion + 'CONVERT_COMPLETE' => 'Conversion completed', + 'CONVERT_COMPLETE_EXPLAIN' => 'You have now successfully converted your board to phpBB 3.2. You can now login and <a href="../">access your board</a>. Please ensure that the settings were transferred correctly before enabling your board by deleting the install directory. Remember that help on using phpBB is available online via the <a href="https://www.phpbb.com/support/docs/en/3.2/ug/">Documentation</a> and the <a href="https://www.phpbb.com/community/viewforum.php?f=466">support forums</a>.', + + 'CONV_ERROR_ATTACH_FTP_DIR' => 'FTP upload for attachments is enabled at the old board. Please disable the FTP upload option and make sure a valid upload directory is specified, then copy all attachment files to this new web accessible directory. Once you have done this, restart the convertor.', + 'CONV_ERROR_CONFIG_EMPTY' => 'There is no configuration information available for the conversion.', + 'CONV_ERROR_FORUM_ACCESS' => 'Unable to get forum access information.', + 'CONV_ERROR_GET_CATEGORIES' => 'Unable to get categories.', + 'CONV_ERROR_GET_CONFIG' => 'Could not retrieve your board configuration.', + 'CONV_ERROR_COULD_NOT_READ' => 'Unable to access/read “%s”.', + 'CONV_ERROR_GROUP_ACCESS' => 'Unable to get group authentication information.', + 'CONV_ERROR_INCONSISTENT_GROUPS' => 'Inconsistency in groups table detected in add_bots() - you need to add all special groups if you do it manually.', + 'CONV_ERROR_INSERT_BOT' => 'Unable to insert bot into users table.', + 'CONV_ERROR_INSERT_BOTGROUP' => 'Unable to insert bot into bots table.', + 'CONV_ERROR_INSERT_USER_GROUP' => 'Unable to insert user into user_group table.', + 'CONV_ERROR_MESSAGE_PARSER' => 'Message parser error', + 'CONV_ERROR_NO_AVATAR_PATH' => 'Note to developer: you must specify $convertor[\'avatar_path\'] to use %s.', + 'CONV_ERROR_NO_FORUM_PATH' => 'The relative path to the source board has not been specified.', + 'CONV_ERROR_NO_GALLERY_PATH' => 'Note to developer: you must specify $convertor[\'avatar_gallery_path\'] to use %s.', + 'CONV_ERROR_NO_GROUP' => 'Group “%1$s” could not be found in %2$s.', + 'CONV_ERROR_NO_RANKS_PATH' => 'Note to developer: you must specify $convertor[\'ranks_path\'] to use %s.', + 'CONV_ERROR_NO_SMILIES_PATH' => 'Note to developer: you must specify $convertor[\'smilies_path\'] to use %s.', + 'CONV_ERROR_NO_UPLOAD_DIR' => 'Note to developer: you must specify $convertor[\'upload_path\'] to use %s.', + 'CONV_ERROR_PERM_SETTING' => 'Unable to insert/update permission setting.', + 'CONV_ERROR_PM_COUNT' => 'Unable to select folder pm count.', + 'CONV_ERROR_REPLACE_CATEGORY' => 'Unable to insert new forum replacing old category.', + 'CONV_ERROR_REPLACE_FORUM' => 'Unable to insert new forum replacing old forum.', + 'CONV_ERROR_USER_ACCESS' => 'Unable to get user authentication information.', + 'CONV_ERROR_WRONG_GROUP' => 'Wrong group “%1$s” defined in %2$s.', + 'CONV_OPTIONS_BODY' => 'This page collects the data required to access the source board. Enter the database details of your former board; the converter will not change anything in the database given below. The source board should be disabled to allow a consistent conversion.', + 'CONV_SAVED_MESSAGES' => 'Saved messages', + + 'PRE_CONVERT_COMPLETE' => 'All pre-conversion steps have successfully been completed. You may now begin the actual conversion process. Please note that you may have to manually do and adjust several things. After conversion, especially check the permissions assigned, rebuild your search index which is not converted and also make sure files got copied correctly, for example avatars and smilies.', +)); diff --git a/phpBB/phpbb/install/controller/helper.php b/phpBB/phpbb/install/controller/helper.php index 2dad42b4b6..6859414236 100644 --- a/phpBB/phpbb/install/controller/helper.php +++ b/phpBB/phpbb/install/controller/helper.php @@ -160,12 +160,13 @@ class helper * Returns path from route name * * @param string $route_name + * @param array $parameters * * @return string */ - public function route($route_name) + public function route($route_name, $parameters = array()) { - $url = $this->router->generate($route_name); + $url = $this->router->generate($route_name, $parameters); return $url; } diff --git a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php index 1342ffa30f..31474ae4e9 100644 --- a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php +++ b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php @@ -72,6 +72,11 @@ class ajax_iohandler extends iohandler_base protected $download; /** + * @var array + */ + protected $redirect_url; + + /** * Constructor * * @param path_helper $path_helper @@ -89,6 +94,7 @@ class ajax_iohandler extends iohandler_base $this->nav_data = array(); $this->cookies = array(); $this->download = array(); + $this->redirect_url = array(); $this->file_status = ''; parent::__construct(); @@ -131,6 +137,14 @@ class ajax_iohandler extends iohandler_base */ public function add_user_form_group($title, $form) { + $this->form = $this->generate_form_render_data($title, $form); + } + + /** + * {@inheritdoc} + */ + public function generate_form_render_data($title, $form) + { $this->template->assign_block_vars('options', array( 'LEGEND' => $this->language->lang($title), 'S_LEGEND' => true, @@ -189,7 +203,7 @@ class ajax_iohandler extends iohandler_base 'form_install' => 'installer_form.html', )); - $this->form = $this->template->assign_display('form_install'); + return $this->template->assign_display('form_install'); } /** @@ -273,6 +287,12 @@ class ajax_iohandler extends iohandler_base $this->cookies = array(); } + if (!empty($this->redirect_url)) + { + $json_array['redirect'] = $this->redirect_url; + $this->redirect_url = array(); + } + return $json_array; } @@ -373,6 +393,15 @@ class ajax_iohandler extends iohandler_base } /** + * {@inheritdoc} + */ + public function redirect($url, $use_ajax = false) + { + $this->redirect_url = array('url' => $url, 'use_ajax' => $use_ajax); + $this->send_response(); + } + + /** * Callback function for language replacing * * @param array $matches diff --git a/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php b/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php index 89f3594378..7945904524 100644 --- a/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php +++ b/phpBB/phpbb/install/helper/iohandler/cli_iohandler.php @@ -289,4 +289,11 @@ class cli_iohandler extends iohandler_base public function render_update_file_status($status_array) { } + + /** + * {@inheritdoc} + */ + public function redirect($url, $use_ajax = false) + { + } } diff --git a/phpBB/phpbb/install/helper/iohandler/iohandler_base.php b/phpBB/phpbb/install/helper/iohandler/iohandler_base.php index 7271fe9bc0..fed4bc101f 100644 --- a/phpBB/phpbb/install/helper/iohandler/iohandler_base.php +++ b/phpBB/phpbb/install/helper/iohandler/iohandler_base.php @@ -170,6 +170,14 @@ abstract class iohandler_base implements iohandler_interface } /** + * {@inheritdoc} + */ + public function generate_form_render_data($title, $form) + { + return ''; + } + + /** * Localize message. * * Note: When an array is passed into the parameters below, it will be diff --git a/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php b/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php index 00aab3283e..6b3839506f 100644 --- a/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php +++ b/phpBB/phpbb/install/helper/iohandler/iohandler_interface.php @@ -124,6 +124,16 @@ interface iohandler_interface public function add_user_form_group($title, $form); /** + * Returns the rendering information for the form + * + * @param string $title Language variable with the title of the form + * @param array $form An array describing the required data (options etc) + * + * @return string Information to render the form + */ + public function generate_form_render_data($title, $form); + + /** * Sets the number of tasks belonging to the installer in the current mode. * * @param int $task_count Number of tasks @@ -175,6 +185,14 @@ interface iohandler_interface public function add_download_link($route, $title, $msg = null); /** + * Redirects the user to a new page + * + * @param string $url URL to redirect to + * @param bool $use_ajax Whether or not to use AJAX redirect + */ + public function redirect($url, $use_ajax = false); + + /** * Renders the status of update files * * @param array $status_array Array containing files in groups to render diff --git a/phpBB/phpbb/install/helper/navigation/convertor_navigation.php b/phpBB/phpbb/install/helper/navigation/convertor_navigation.php new file mode 100644 index 0000000000..54cab83b1d --- /dev/null +++ b/phpBB/phpbb/install/helper/navigation/convertor_navigation.php @@ -0,0 +1,78 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\install\helper\navigation; + +use phpbb\install\helper\install_helper; + +class convertor_navigation implements navigation_interface +{ + /** + * @var install_helper + */ + private $install_helper; + + /** + * Constructor + * + * @param install_helper $install_helper + */ + public function __construct(install_helper $install_helper) + { + $this->install_helper = $install_helper; + } + + /** + * {@inheritdoc} + */ + public function get() + { + if (!$this->install_helper->is_phpbb_installed()) + { + return array(); + } + + return array( + 'convert' => array( + 'label' => 'CONVERT', + 'route' => 'phpbb_convert_intro', + 'order' => 3, + array( + 'intro' => array( + 'label' => 'SUB_INTRO', + 'stage' => true, + 'order' => 0, + ), + 'settings' => array( + 'label' => 'STAGE_SETTINGS', + 'stage' => true, + 'route' => 'phpbb_convert_settings', + 'order' => 1, + ), + 'convert' => array( + 'label' => 'STAGE_IN_PROGRESS', + 'stage' => true, + 'route' => 'phpbb_convert_convert', + 'order' => 2, + ), + 'finish' => array( + 'label' => 'CONVERT_COMPLETE', + 'stage' => true, + 'route' => 'phpbb_convert_finish', + 'order' => 3, + ), + ), + ), + ); + } +} |