diff --git a/phpBB/adm/style/admin.css b/phpBB/adm/style/admin.css index 145bcfaec1..dd5878a976 100644 --- a/phpBB/adm/style/admin.css +++ b/phpBB/adm/style/admin.css @@ -1776,7 +1776,7 @@ li.pagination ul { /* Action Highlighting ---------------------------------------- */ -.successbox, .errorbox { +.successbox, .errorbox, .warningbox { padding: 8px; margin: 10px 0; color: #FFFFFF; @@ -1800,6 +1800,10 @@ li.pagination ul { background-color: #BC2A4D; } +.warningbox { + background-color: #fca600; +} + .successbox h3, .errorbox h3 { color: #FFFFFF; margin: 0 0 0.5em; diff --git a/phpBB/adm/style/installer_form.html b/phpBB/adm/style/installer_form.html new file mode 100644 index 0000000000..dbcf3c19f8 --- /dev/null +++ b/phpBB/adm/style/installer_form.html @@ -0,0 +1,52 @@ +
+ +
+ + + + +
+ +
+ + {options.LEGEND} + + + +
+

{options.TITLE_EXPLAIN}
+
+ + + + + + + + + + + + + + + checked /> {options.OPTIONS.label} + + +
+
+ +
+
+ {L_SUBMIT} + +
+ + + + +
diff --git a/phpBB/adm/style/installer_install.html b/phpBB/adm/style/installer_install.html new file mode 100644 index 0000000000..7d62075c62 --- /dev/null +++ b/phpBB/adm/style/installer_install.html @@ -0,0 +1,13 @@ + +

{L_INSTALL}

+{CONTENT} + +
+
+ {L_SUBMIT} + +
+
+ + + diff --git a/phpBB/assets/javascript/installer.js b/phpBB/assets/javascript/installer.js new file mode 100644 index 0000000000..302d95e7c6 --- /dev/null +++ b/phpBB/assets/javascript/installer.js @@ -0,0 +1,205 @@ +/** + * Installer's AJAX frontend handler + */ + +(function($) { // Avoid conflicts with other libraries + // Global variables + var pollTimer = null; + var nextReadPosition = 0; + + // Template related variables + var $contentWrapper = $('.install-body').find('.main'); + + // Intercept form submits + intercept_form_submit($('#install_install')); + + function poll_content(xhReq) { + var messages = xhReq.responseText; + + do { + var unprocessed = messages.substring(nextReadPosition); + var messageEndIndex = unprocessed.indexOf('}\n\n'); + + if (messageEndIndex !== -1) { + var endOfMessageIndex = messageEndIndex + 3; // 3 is the length of "}\n\n" + var message = unprocessed.substring(0, endOfMessageIndex); + parse_message(message); + nextReadPosition += endOfMessageIndex; + } + } while (messageEndIndex !== -1); + + if (xhReq.readyState === 4) { + $('#loading_indicator').css('display', 'none'); + reset_polling(); + } + } + + function parse_message(messageJSON) { + $('#loading_indicator').css('display', 'none'); + + messageJSON = messageJSON.trim(); + var responseObject = JSON.parse(messageJSON); + + // Parse object + if (responseObject.hasOwnProperty('errors')) { + add_message('error', responseObject.errors) + } + + if (responseObject.hasOwnProperty('warnings')) { + add_message('warning', responseObject.warnings) + } + + if (responseObject.hasOwnProperty('logs')) { + add_message('log', responseObject.logs); + } + + if (responseObject.hasOwnProperty('form')) { + add_form(responseObject.form); + } + } + + function add_message(type, messages) { + // Get message containers + var errorContainer = $('#error-container'); + var warningContainer = $('#warning-container'); + var logContainer = $('#log-container'); + + var title, description, msgElement, arraySize = messages.length; + for (var i = 0; i < arraySize; i++) { + msgElement = $('
'); + title = $(document.createElement('strong')); + title.text(messages[i].title); + msgElement.append(title); + + if (messages[i].hasOwnProperty('description')) { + description = $(document.createElement('p')); + description.text(messages[i].description); + msgElement.append(description); + } + + switch (type) { + case 'error': + msgElement.addClass('errorbox'); + errorContainer.append(msgElement); + break; + case 'warning': + msgElement.addClass('warningbox'); + warningContainer.append(msgElement); + break; + case 'log': + msgElement.addClass('log'); + logContainer.append(msgElement); + break; + } + } + } + + function add_form(formHtml) { + var formContainer = $('#content-container'); + formContainer.html(formHtml); + var form = $('#install_install'); + intercept_form_submit(form); + } + + function start_polling(xhReq) { + reset_polling(); + pollTimer = setInterval(function () { + poll_content(xhReq); + }, 500); + } + + function reset_polling() { + clearInterval(pollTimer); + nextReadPosition = 0; + } + + function submit_form(form, submitBtn) { + form.css('display', 'none'); + + var xhReq = create_xhr_object(); + xhReq.open('POST', form.attr('action'), true); + xhReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhReq.send(get_form_fields(form, submitBtn)); + + // Clear content + setup_ajax_layout(); + $('#loading_indicator').css('display', 'block'); + + start_polling(xhReq); + } + + // Workaround for submit buttons + function get_form_fields(form, submitBtn) { + var formData = form.serialize(); + //var submitBtn = form.find(':submit'); + formData += ((formData.length) ? '&' : '') + encodeURIComponent(submitBtn.attr('name')) + '='; + formData += encodeURIComponent(submitBtn.attr('value')); + + return formData; + } + + function intercept_form_submit(form) { + if (!form.length) { + return; + } + + form.find(':submit').bind('click', function (event) { + event.preventDefault(); + submit_form(form, $(this)); + }); + + } + + /** + * jQuery cannot be used as the response is streamed, and + * as of now, jQuery does not provide access to the response until + * the connection is not closed. + */ + function create_xhr_object() { + var xhReq; + + if (window.XMLHttpRequest) { + xhReq = new XMLHttpRequest(); + } + else if (window.ActiveXObject) { + xhReq = new ActiveXObject("Msxml2.XMLHTTP"); + } + + return xhReq; + } + + function setup_ajax_layout() { + // Clear content + $contentWrapper.html(''); + + var $header = $('
'); + $header.attr('id', 'header-container'); + $contentWrapper.append($header); + + var $description = $('
'); + $description.attr('id', 'description-container'); + $contentWrapper.append($description); + + var $errorContainer = $('
'); + $errorContainer.attr('id', 'error-container'); + $contentWrapper.append($errorContainer); + + var $warningContainer = $('
'); + $warningContainer.attr('id', 'warning-container'); + $contentWrapper.append($warningContainer); + + var $installerContentWrapper = $('
'); + $installerContentWrapper.attr('id', 'content-container'); + $contentWrapper.append($installerContentWrapper); + + var $logContainer = $('
'); + $logContainer.attr('id', 'log-container'); + $contentWrapper.append($logContainer); + + var $spinner = $('
'); + $spinner.attr('id', 'loading_indicator'); + $spinner.html(' '); + $contentWrapper.append($spinner); + } +})(jQuery); // Avoid conflicts with other libraries diff --git a/phpBB/config/installer/routing/environment.yml b/phpBB/config/installer/routing/environment.yml new file mode 100644 index 0000000000..60324c975b --- /dev/null +++ b/phpBB/config/installer/routing/environment.yml @@ -0,0 +1,2 @@ +core.default: + resource: "installer.yml" diff --git a/phpBB/config/installer/routing/installer.yml b/phpBB/config/installer/routing/installer.yml new file mode 100644 index 0000000000..80a995ab6e --- /dev/null +++ b/phpBB/config/installer/routing/installer.yml @@ -0,0 +1,22 @@ +phpbb_installer_index: + path: / + defaults: + _controller: phpbb.installer.controller.welcome:handle + mode: "intro" + +phpbb_installer_license: + path: /license + defaults: + _controller: phpbb.installer.controller.welcome:handle + mode: "license" + +phpbb_installer_support: + path: /support + defaults: + _controller: phpbb.installer.controller.welcome:handle + mode: "support" + +phpbb_installer_install: + path: /install + defaults: + _controller: phpbb.installer.controller.install:handle diff --git a/phpBB/install/app.php b/phpBB/install/app.php new file mode 100644 index 0000000000..58ca141af7 --- /dev/null +++ b/phpBB/install/app.php @@ -0,0 +1,83 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +/** + * @ignore + */ +define('IN_PHPBB', true); +define('IN_INSTALL', true); +define('PHPBB_ENVIRONMENT', 'production'); +$phpbb_root_path = '../'; +$phpEx = substr(strrchr(__FILE__, '.'), 1); + +// +// Let's do the common.php logic +// +require($phpbb_root_path . 'includes/startup.' . $phpEx); +require($phpbb_root_path . 'phpbb/class_loader.' . $phpEx); + +$phpbb_class_loader = new \phpbb\class_loader('phpbb\\install\\', "{$phpbb_root_path}install/", $phpEx); +$phpbb_class_loader->register(); + +$phpbb_class_loader = new \phpbb\class_loader('phpbb\\', "{$phpbb_root_path}phpbb/", $phpEx); +$phpbb_class_loader->register(); + +// In case $phpbb_adm_relative_path is not set (in case of an update), use the default. +$phpbb_adm_relative_path = (isset($phpbb_adm_relative_path)) ? $phpbb_adm_relative_path : 'adm/'; +$phpbb_admin_path = (defined('PHPBB_ADMIN_PATH')) ? PHPBB_ADMIN_PATH : $phpbb_root_path . $phpbb_adm_relative_path; + +// Include files +require($phpbb_root_path . 'includes/functions.' . $phpEx); +require($phpbb_root_path . 'includes/functions_content.' . $phpEx); +include($phpbb_root_path . 'includes/functions_compatibility.' . $phpEx); +require($phpbb_root_path . 'includes/functions_user.' . $phpEx); +require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx); + +// Set PHP error handler to ours +set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler'); + +$phpbb_installer_container_builder = new \phpbb\di\container_builder($phpbb_root_path, $phpEx); +$phpbb_installer_container = $phpbb_installer_container_builder + ->with_environment('installer') + ->without_extensions() + ->without_cache() + ->get_container(); + +// Path to templates +$paths = array($phpbb_root_path . 'install/update/new/adm/style', $phpbb_admin_path . 'style'); +$paths = array_filter($paths, 'is_dir'); + +/** @var \phpbb\filesystem\filesystem $phpbb_filesystem */ +$phpbb_filesystem = $phpbb_installer_container->get('filesystem'); + +/** @var \phpbb\template\template $template */ +$template = $phpbb_installer_container->get('template'); +$template->set_custom_style(array( + array( + 'name' => 'adm', + 'ext_path' => 'adm/style/', + ), +), $paths); + +/** @var \phpbb\language\language $language */ +$language = $phpbb_installer_container->get('language'); +$language->add_lang(array('common', 'acp/common', 'acp/board', 'install', 'posting')); + +/* @var $http_kernel \Symfony\Component\HttpKernel\HttpKernel */ +$http_kernel = $phpbb_installer_container->get('http_kernel'); + +/* @var $symfony_request \phpbb\symfony_request */ +$symfony_request = $phpbb_installer_container->get('symfony_request'); +$response = $http_kernel->handle($symfony_request); +$response->send(); +$http_kernel->terminate($symfony_request, $response); diff --git a/phpBB/install/controller/helper.php b/phpBB/install/controller/helper.php new file mode 100644 index 0000000000..7a5e20406d --- /dev/null +++ b/phpBB/install/controller/helper.php @@ -0,0 +1,228 @@ + + * @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\controller; + +use Symfony\Component\HttpFoundation\Response; + +/** + * A duplicate of \phpbb\controller\helper + * + * This class is necessary because of controller\helper's legacy function calls + * to page_header() page_footer() functions which has unavailable dependencies. + */ +class helper +{ + /** + * @var \phpbb\language\language + */ + protected $language; + + /** + * @var \phpbb\language\language_file_helper + */ + protected $lang_helper; + + /** + * @var \phpbb\install\helper\navigation\navigation_provider + */ + protected $navigation_provider; + + /** + * @var \phpbb\template\template + */ + protected $template; + + /** + * @var \phpbb\path_helper + */ + protected $path_helper; + + /** + * @var \phpbb\symfony_request + */ + protected $request; + + /** + * @var \phpbb\routing\router + */ + protected $router; + + /** + * @var string + */ + protected $phpbb_admin_path; + + /** + * @var string + */ + protected $phpbb_root_path; + + public function __construct(\phpbb\language\language $language, \phpbb\language\language_file_helper $lang_helper, \phpbb\install\helper\navigation\navigation_provider $nav, \phpbb\template\template $template, \phpbb\path_helper $path_helper, \phpbb\symfony_request $request, \phpbb\routing\router $router, $phpbb_root_path) + { + $this->language = $language; + $this->lang_helper = $lang_helper; + $this->navigation_provider = $nav; + $this->template = $template; + $this->path_helper = $path_helper; + $this->request = $request; + $this->router = $router; + $this->phpbb_root_path = $phpbb_root_path; + $this->phpbb_admin_path = $phpbb_root_path . 'adm/'; + } + + /** + * Automate setting up the page and creating the response object. + * + * @param string $template_file The template handle to render + * @param string $page_title The title of the page to output + * @param int $status_code The status code to be sent to the page header + * + * @return Response object containing rendered page + */ + public function render($template_file, $page_title = '', $status_code = 200) + { + $this->page_header($page_title); + + $this->template->set_filenames(array( + 'body' => $template_file, + )); + + return new Response($this->template->assign_display('body'), $status_code); + } + + /** + * Set default template variables + * + * @param string $page_title + */ + protected function page_header($page_title) + { + $this->template->assign_vars(array( + 'L_CHANGE' => $this->language->lang('CHANGE'), + 'L_COLON' => $this->language->lang('COLON'), + 'L_INSTALL_PANEL' => $this->language->lang('INSTALL_PANEL'), + 'L_SELECT_LANG' => $this->language->lang('SELECT_LANG'), + 'L_SKIP' => $this->language->lang('SKIP'), + 'PAGE_TITLE' => $this->language->lang($page_title), + 'T_IMAGE_PATH' => htmlspecialchars($this->phpbb_admin_path) . 'images/', + 'T_JQUERY_LINK' => $this->path_helper->get_web_root_path() . 'assets/javascript/jquery.min.js', + 'T_TEMPLATE_PATH' => $this->path_helper->get_web_root_path() . 'adm/style', + 'T_ASSETS_PATH' => $this->path_helper->get_web_root_path() . 'assets/', + + 'S_CONTENT_DIRECTION' => $this->language->lang('DIRECTION'), + 'S_CONTENT_FLOW_BEGIN' => ($this->language->lang('DIRECTION') === 'ltr') ? 'left' : 'right', + 'S_CONTENT_FLOW_END' => ($this->language->lang('DIRECTION') === 'ltr') ? 'right' : 'left', + 'S_CONTENT_ENCODING' => 'UTF-8', + + 'S_USER_LANG' => $this->language->lang('USER_LANG'), + ) + ); + + $this->render_navigation(); + } + + /** + * Render navigation + */ + protected function render_navigation() + { + // Get navigation items + $nav_array = $this->navigation_provider->get(); + + // @todo Sort navs by order + + $active_main_menu = $this->get_active_main_menu($nav_array); + + // Pass navigation to template + foreach ($nav_array as $key => $entry) + { + $this->template->assign_block_vars('t_block1', array( + 'L_TITLE' => $this->language->lang($entry['label']), + 'S_SELECTED' => ($active_main_menu === $key), + 'U_TITLE' => $this->route($entry['route']), + )); + + if (is_array($entry[0]) && $active_main_menu === $key) + { + // @todo Sort navs by order + + foreach ($entry[0] as $sub_entry) + { + $this->template->assign_block_vars('l_block1', array( + 'L_TITLE' => $this->language->lang($sub_entry['label']), + 'S_SELECTED' => (isset($sub_entry['route']) && $sub_entry['route'] === $this->request->get('_route')), + 'U_TITLE' => $this->route($sub_entry['route']), + )); + } + } + } + } + + /** + * Returns path from route name + * + * @param string $route_name + * + * @return string + */ + public function route($route_name) + { + $url = $this->router->generate($route_name); + + return $url; + } + + /** + * Render language select form + */ + protected function render_language_select() + { + $langs = $this->lang_helper->get_available_languages(); + } + + /** + * Returns the name of the active main menu item + * + * @param array $nav_array + * + * @return string|bool Returns the name of the active main menu element, if the element not found, returns false + */ + protected function get_active_main_menu($nav_array) + { + $active_route = $this->request->get('_route'); + + foreach ($nav_array as $nav_name => $nav_options) + { + $current_menu = $nav_name; + + if (isset($nav_options['route']) && $nav_options['route'] === $active_route) + { + return $nav_name; + } + + if (is_array($nav_options[0])) + { + foreach ($nav_options[0] as $sub_menus) + { + if (isset($sub_menus['route']) &&$sub_menus['route'] === $active_route) + { + return $current_menu; + } + } + } + } + + return false; + } +} diff --git a/phpBB/install/controller/install.php b/phpBB/install/controller/install.php new file mode 100644 index 0000000000..1217107484 --- /dev/null +++ b/phpBB/install/controller/install.php @@ -0,0 +1,109 @@ + + * @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\controller; + +use Symfony\Component\HttpFoundation\StreamedResponse; + +/** + * Controller for installing phpBB + */ +class install +{ + /** + * @var \phpbb\install\controller\helper + */ + protected $controller_helper; + + /** + * @var \phpbb\install\helper\iohandler\factory + */ + protected $iohandler_factory; + + /** + * @var \phpbb\template\template + */ + protected $template; + + /** + * @var \phpbb\request\request_interface + */ + protected $request; + + /** + * @var \phpbb\install\installer + */ + protected $installer; + + /** + * Constructor + * + * @param helper $helper + * @param \phpbb\install\helper\iohandler\factory $factory + * @param \phpbb\request\request_interface $request + * @param \phpbb\install\installer $installer + */ + public function __construct(helper $helper, \phpbb\install\helper\iohandler\factory $factory, \phpbb\template\template $template, \phpbb\request\request_interface $request, \phpbb\install\installer $installer) + { + $this->controller_helper = $helper; + $this->iohandler_factory = $factory; + $this->template = $template; + $this->request = $request; + $this->installer = $installer; + } + + public function handle() + { + // @todo check that phpBB is not already installed + + $this->template->assign_vars(array( + 'U_ACTION' => $this->controller_helper->route('phpbb_installer_install'), + )); + + // Set up input-output handler + if ($this->request->is_ajax()) + { + $this->iohandler_factory->set_environment('ajax'); + } + else + { + $this->iohandler_factory->set_environment('nojs'); + } + + if ($this->request->is_ajax()) + { + $installer = &$this->installer; + + $response = new StreamedResponse(); + $response->setCallback(function() use ($installer) { + $installer->run(); + }); + + return $response; + } + else + { + // Determine whether the installation was started or not + if (true) + { + // If not, let's render the welcome page + $this->template->assign_vars(array( + 'SHOW_INSTALL_START_FORM' => true, + )); + return $this->controller_helper->render('installer_install.html', 'INSTALL'); + } + + // @todo: implement no js controller logic + } + } +} diff --git a/phpBB/install/controller/install_index.php b/phpBB/install/controller/install_index.php new file mode 100644 index 0000000000..c61d68f7fb --- /dev/null +++ b/phpBB/install/controller/install_index.php @@ -0,0 +1,79 @@ + + * @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\controller; + +class install_index +{ + /** + * @var helper + */ + protected $helper; + + /** + * @var \phpbb\language\language + */ + protected $language; + + /** + * @var \phpbb\template\template + */ + protected $template; + + /** + * @var string + */ + protected $phpbb_root_path; + + /** + * Constructor + * + * @param helper $helper + * @param \phpbb\language\language $language + * @param \phpbb\template\template $template + * @param string $phpbb_root_path + */ + public function __construct(helper $helper, \phpbb\language\language $language, \phpbb\template\template $template, $phpbb_root_path) + { + $this->helper = $helper; + $this->language = $language; + $this->template = $template; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function handle($mode) + { + switch ($mode) + { + case "intro": + $title = $this->language->lang('INTRODUCTION_TITLE'); + $body = $this->language->lang('INTRODUCTION_BODY'); + break; + case "support": + $title = $this->language->lang('SUPPORT_TITLE'); + $body = $this->language->lang('SUPPORT_BODY'); + break; + case "license": + $title = $this->language->lang('LICENSE_TITLE'); + $body = implode("
\n", file($this->phpbb_root_path . 'docs/LICENSE.txt')); + break; + } + + $this->template->assign_vars(array( + 'TITLE' => $title, + 'BODY' => $body, + )); + + return $this->helper->render('install_main.html', $title); + } +}