diff --git a/phpBB/adm/style/acp_ext_enable.html b/phpBB/adm/style/acp_ext_enable.html index 3f7be2c847..35585207eb 100644 --- a/phpBB/adm/style/acp_ext_enable.html +++ b/phpBB/adm/style/acp_ext_enable.html @@ -7,7 +7,13 @@

{L_EXTENSIONS_EXPLAIN}

{L_ENABLE_EXPLAIN}

- + +
+

{L_MIGRATION_EXCEPTION_ERROR}

+

{MIGRATOR_ERROR}

+

{L_RETURN}

+
+

{L_ENABLE_CONFIRM}

diff --git a/phpBB/adm/style/acp_ext_purge.html b/phpBB/adm/style/acp_ext_purge.html index 00a58721cb..94bef82ca5 100644 --- a/phpBB/adm/style/acp_ext_purge.html +++ b/phpBB/adm/style/acp_ext_purge.html @@ -7,7 +7,13 @@

{L_EXTENSIONS_EXPLAIN}

{L_PURGE_EXPLAIN}

- + +
+

{L_MIGRATION_EXCEPTION_ERROR}

+

{MIGRATOR_ERROR}

+

{L_RETURN}

+
+

{L_PURGE_CONFIRM}

diff --git a/phpBB/config/services.yml b/phpBB/config/services.yml index ee52ca2800..ef5359129f 100644 --- a/phpBB/config/services.yml +++ b/phpBB/config/services.yml @@ -112,8 +112,10 @@ services: ext.manager: class: phpbb_extension_manager arguments: + - @service_container - @dbal.conn - @config + - @migrator - %tables.ext% - %core.root_path% - .%core.php_ext% diff --git a/phpBB/includes/acp/acp_extensions.php b/phpBB/includes/acp/acp_extensions.php index a0bcf62ecc..24211196bd 100644 --- a/phpBB/includes/acp/acp_extensions.php +++ b/phpBB/includes/acp/acp_extensions.php @@ -37,7 +37,7 @@ class acp_extensions $this->template = $template; $this->user = $user; - $user->add_lang(array('install', 'acp/extensions')); + $user->add_lang(array('install', 'acp/extensions', 'migrator')); $this->page_title = 'ACP_EXTENSIONS'; @@ -103,11 +103,18 @@ class acp_extensions trigger_error($user->lang['EXTENSION_NOT_AVAILABLE'] . adm_back_link($this->u_action)); } - if ($phpbb_extension_manager->enable_step($ext_name)) + try { - $template->assign_var('S_NEXT_STEP', true); + if ($phpbb_extension_manager->enable_step($ext_name)) + { + $template->assign_var('S_NEXT_STEP', true); - meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); + meta_refresh(0, $this->u_action . '&action=enable&ext_name=' . urlencode($ext_name)); + } + } + catch (phpbb_db_migration_exception $e) + { + $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user)); } $this->tpl_name = 'acp_ext_enable'; @@ -156,11 +163,18 @@ class acp_extensions break; case 'purge': - if ($phpbb_extension_manager->purge_step($ext_name)) + try { - $template->assign_var('S_NEXT_STEP', true); + if ($phpbb_extension_manager->purge_step($ext_name)) + { + $template->assign_var('S_NEXT_STEP', true); - meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); + meta_refresh(0, $this->u_action . '&action=purge&ext_name=' . urlencode($ext_name)); + } + } + catch (phpbb_db_migration_exception $e) + { + $template->assign_var('MIGRATOR_ERROR', $e->getLocalisedMessage($user)); } $this->tpl_name = 'acp_ext_purge'; diff --git a/phpBB/includes/db/db_tools.php b/phpBB/includes/db/db_tools.php index e8c26fa502..983cdc18ea 100644 --- a/phpBB/includes/db/db_tools.php +++ b/phpBB/includes/db/db_tools.php @@ -303,7 +303,7 @@ class phpbb_db_tools * @param phpbb_db_driver $db Database connection * @param bool $return_statements True if only statements should be returned and no SQL being executed */ - function phpbb_db_tools(&$db, $return_statements = false) + function phpbb_db_tools(phpbb_db_driver $db, $return_statements = false) { $this->db = $db; $this->return_statements = $return_statements; diff --git a/phpBB/includes/db/migration/data/310/dev.php b/phpBB/includes/db/migration/data/310/dev.php index 34f081bf62..13b36bbf30 100644 --- a/phpBB/includes/db/migration/data/310/dev.php +++ b/phpBB/includes/db/migration/data/310/dev.php @@ -21,7 +21,6 @@ class phpbb_db_migration_data_310_dev extends phpbb_db_migration 'phpbb_db_migration_data_310_style_update_p2', 'phpbb_db_migration_data_310_timezone_p2', 'phpbb_db_migration_data_310_reported_posts_display', - 'phpbb_db_migration_data_310_remove_msnm', ); } diff --git a/phpBB/includes/db/migration/data/310/remove_msnm.php b/phpBB/includes/db/migration/data/310/remove_msnm.php deleted file mode 100644 index 521161df6b..0000000000 --- a/phpBB/includes/db/migration/data/310/remove_msnm.php +++ /dev/null @@ -1,43 +0,0 @@ -db_tools->sql_column_exists($this->table_prefix . 'users', 'user_msnm'); - } - - static public function depends_on() - { - return array('phpbb_db_migration_data_30x_3_0_11'); - } - - public function update_schema() - { - return array( - 'drop_columns' => array( - $this->table_prefix . 'users' => array( - 'user_msnm', - ), - ), - ); - } - - public function revert_schema() - { - return array( - 'add_columns' => array( - $this->table_prefix . 'users' => array( - 'user_msnm' => array('VCHAR_UNI', ''), - ), - ), - ); - } -} diff --git a/phpBB/includes/db/migration/exception.php b/phpBB/includes/db/migration/exception.php index ffdcd97780..e84330dd71 100644 --- a/phpBB/includes/db/migration/exception.php +++ b/phpBB/includes/db/migration/exception.php @@ -52,4 +52,28 @@ class phpbb_db_migration_exception extends \Exception { return $this->message . ': ' . var_export($this->parameters, true); } + + /** + * Get the parameters + * + * @return array + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Get localised message (with $user->lang()) + * + * @param phpbb_user $user + * @return string + */ + public function getLocalisedMessage(phpbb_user $user) + { + $parameters = $this->getParameters(); + array_unshift($parameters, $this->getMessage()); + + return call_user_func_array(array($user, 'lang'), $parameters); + } } diff --git a/phpBB/includes/db/migration/tool/config.php b/phpBB/includes/db/migration/tool/config.php index d9cc20053e..458a25fb66 100644 --- a/phpBB/includes/db/migration/tool/config.php +++ b/phpBB/includes/db/migration/tool/config.php @@ -49,7 +49,7 @@ class phpbb_db_migration_tool_config implements phpbb_db_migration_tool_interfac { if (isset($this->config[$config_name])) { - throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXISTS', $config_name); + throw new phpbb_db_migration_exception('CONFIG_ALREADY_EXIST', $config_name); } $this->config->set($config_name, $config_value, !$is_dynamic); diff --git a/phpBB/includes/db/migration/tool/module.php b/phpBB/includes/db/migration/tool/module.php index afe1f21ec5..4d7fae2bb0 100644 --- a/phpBB/includes/db/migration/tool/module.php +++ b/phpBB/includes/db/migration/tool/module.php @@ -242,14 +242,14 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac if (!$module_id) { - throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent); + throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent); } $parent = $data['parent_id'] = $module_id; } else if (!$this->exists($class, false, $parent)) { - throw new phpbb_db_migration_exception('MODULE_PARENT_NOT_EXIST', $parent); + throw new phpbb_db_migration_exception('MODULE_NOT_EXIST', $parent); } if ($this->exists($class, $parent, $data['module_langname'])) @@ -477,7 +477,7 @@ class phpbb_db_migration_tool_module implements phpbb_db_migration_tool_interfac $result = $acp_modules->delete_module($module_id); if (!empty($result)) { - throw new phpbb_db_migration_exception('CANNOT_REMOVE_MODULE', $module_id); + throw new phpbb_db_migration_exception('MODULE_NOT_REMOVABLE', $module_id, $result); } } diff --git a/phpBB/includes/db/migration/tool/permission.php b/phpBB/includes/db/migration/tool/permission.php index 001d090f5a..4231fbe1dd 100644 --- a/phpBB/includes/db/migration/tool/permission.php +++ b/phpBB/includes/db/migration/tool/permission.php @@ -107,7 +107,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte { if ($this->exists($auth_option, $global)) { - throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXISTS', $auth_option); + throw new phpbb_db_migration_exception('PERMISSION_ALREADY_EXIST', $auth_option); } // We've added permissions, so set to true to notify the user. @@ -252,7 +252,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte if ($role_id) { - throw new phpbb_db_migration_exception('ROLE_ALREADY_EXISTS', $old_role_name); + return; } $sql = 'SELECT MAX(role_order) AS max_role_order @@ -290,7 +290,7 @@ class phpbb_db_migration_tool_permission implements phpbb_db_migration_tool_inte if (!$role_id) { - throw new phpbb_db_migration_exception('ROLE_NOT_EXISTS', $old_role_name); + throw new phpbb_db_migration_exception('ROLE_NOT_EXIST', $old_role_name); } $sql = 'UPDATE ' . ACL_ROLES_TABLE . " diff --git a/phpBB/includes/db/migrator.php b/phpBB/includes/db/migrator.php index 4456600b0a..41d996b1e3 100644 --- a/phpBB/includes/db/migrator.php +++ b/phpBB/includes/db/migrator.php @@ -228,9 +228,10 @@ class phpbb_db_migrator { foreach ($this->migrations as $name) { - if ($this->unfulfillable($name)) + $unfulfillable = $this->unfulfillable($name); + if ($unfulfillable !== false) { - throw new phpbb_db_migration_exception('MIGRATION NOT FULFILLABLE', $name); + throw new phpbb_db_migration_exception('MIGRATION_NOT_FULFILLABLE', $name, $unfulfillable); } } } @@ -674,7 +675,7 @@ class phpbb_db_migrator * Checks if a migration's dependencies can even theoretically be satisfied. * * @param string $name The class name of the migration - * @return bool Whether the migration cannot be fulfilled + * @return bool|string False if fulfillable, string of missing migration name if unfulfillable */ public function unfulfillable($name) { @@ -685,7 +686,7 @@ class phpbb_db_migrator if (!class_exists($name)) { - return true; + return $name; } $migration = $this->get_migration($name); @@ -693,9 +694,10 @@ class phpbb_db_migrator foreach ($depends as $depend) { - if ($this->unfulfillable($depend)) + $unfulfillable = $this->unfulfillable($depend); + if ($unfulfillable !== false) { - return true; + return $unfulfillable; } } @@ -715,7 +717,7 @@ class phpbb_db_migrator { // skip unfulfillable migrations, but fulfillables mean we // are not finished yet - if ($this->unfulfillable($name)) + if ($this->unfulfillable($name) !== false) { continue; } diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php index 9d076eb6c5..d51589d719 100644 --- a/phpBB/includes/extension/base.php +++ b/phpBB/includes/extension/base.php @@ -15,6 +15,8 @@ if (!defined('IN_PHPBB')) exit; } +use Symfony\Component\DependencyInjection\ContainerInterface; + /** * A base class for extensions without custom enable/disable/purge code. * @@ -22,6 +24,19 @@ if (!defined('IN_PHPBB')) */ class phpbb_extension_base implements phpbb_extension_interface { + /** @var ContainerInterface */ + protected $container; + + /** + * Constructor + * + * @param ContainerInterface $container Container object + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + /** * Single enable step that does nothing * diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php index de6f364320..21a9ec1370 100644 --- a/phpBB/includes/extension/manager.php +++ b/phpBB/includes/extension/manager.php @@ -15,6 +15,8 @@ if (!defined('IN_PHPBB')) exit; } +use Symfony\Component\DependencyInjection\ContainerInterface; + /** * The extension manager provides means to activate/deactivate extensions. * @@ -22,8 +24,12 @@ if (!defined('IN_PHPBB')) */ class phpbb_extension_manager { + /** @var ContainerInterface */ + protected $container; + protected $db; protected $config; + protected $migrator; protected $cache; protected $php_ext; protected $extensions; @@ -34,6 +40,7 @@ class phpbb_extension_manager /** * Creates a manager and loads information from database * + * @param ContainerInterface $container A container * @param phpbb_db_driver $db A database connection * @param phpbb_config $config phpbb_config * @param string $extension_table The name of the table holding extensions @@ -42,11 +49,13 @@ class phpbb_extension_manager * @param phpbb_cache_driver_interface $cache A cache instance or null * @param string $cache_name The name of the cache variable, defaults to _ext */ - public function __construct(phpbb_db_driver $db, phpbb_config $config, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext') + public function __construct(ContainerInterface $container, phpbb_db_driver $db, phpbb_config $config, phpbb_db_migrator $migrator, $extension_table, $phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext') { + $this->container = $container; $this->phpbb_root_path = $phpbb_root_path; $this->db = $db; $this->config = $config; + $this->migrator = $migrator; $this->cache = $cache; $this->php_ext = $php_ext; $this->extension_table = $extension_table; @@ -126,11 +135,11 @@ class phpbb_extension_manager if (class_exists($extension_class_name)) { - return new $extension_class_name; + return new $extension_class_name($this->container); } else { - return new phpbb_extension_base; + return new phpbb_extension_base($this->container); } } @@ -166,6 +175,12 @@ class phpbb_extension_manager $old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false; + // Returns false if not completed + if (!$this->handle_migrations($name, 'enable')) + { + return true; + } + $extension = $this->get_extension($name); $state = $extension->enable_step($old_state); @@ -317,6 +332,12 @@ class phpbb_extension_manager $old_state = unserialize($this->extensions[$name]['ext_state']); + // Returns false if not completed + if (!$this->handle_migrations($name, 'purge')) + { + return true; + } + $extension = $this->get_extension($name); $state = $extension->purge_step($old_state); @@ -490,4 +511,58 @@ class phpbb_extension_manager { return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->php_ext, $this->cache_name . '_finder'); } + + /** + * Handle installing/reverting migrations + * + * @param string $extension_name Name of the extension + * @param string $mode enable or purge + * @return bool True if completed, False if not completed + */ + protected function handle_migrations($extension_name, $mode) + { + $migrations_path = $this->phpbb_root_path . $this->get_extension_path($extension_name) . 'migrations/'; + if (!file_exists($migrations_path) || !is_dir($migrations_path)) + { + return true; + } + + $migrations = $this->migrator->load_migrations($migrations_path); + + // What is a safe limit of execution time? Half the max execution time should be safe. + $safe_time_limit = (ini_get('max_execution_time') / 2); + $start_time = time(); + + if ($mode == 'enable') + { + while (!$this->migrator->finished()) + { + $this->migrator->update(); + + // Are we approaching the time limit? If so we want to pause the update and continue after refreshing + if ((time() - $start_time) >= $safe_time_limit) + { + return false; + } + } + } + else if ($mode == 'purge') + { + foreach ($migrations as $migration) + { + while ($this->migrator->migration_state($migration) !== false) + { + $this->migrator->revert($migration); + + // Are we approaching the time limit? If so we want to pause the update and continue after refreshing + if ((time() - $start_time) >= $safe_time_limit) + { + return false; + } + } + } + } + + return true; + } } diff --git a/phpBB/includes/search/base.php b/phpBB/includes/search/base.php index b364dead9a..914cef9167 100644 --- a/phpBB/includes/search/base.php +++ b/phpBB/includes/search/base.php @@ -94,7 +94,7 @@ class phpbb_search_base * * @return int SEARCH_RESULT_NOT_IN_CACHE or SEARCH_RESULT_IN_CACHE or SEARCH_RESULT_INCOMPLETE */ - function obtain_ids($search_key, &$result_count, &$id_ary, $start, $per_page, $sort_dir) + function obtain_ids($search_key, &$result_count, &$id_ary, &$start, $per_page, $sort_dir) { global $cache; @@ -109,6 +109,19 @@ class phpbb_search_base $reverse_ids = ($stored_ids[-2] != $sort_dir) ? true : false; $complete = true; + // Change start parameter in case out of bounds + if ($result_count) + { + if ($start < 0) + { + $start = 0; + } + else if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + } + } + // change the start to the actual end of the current request if the sort direction differs // from the dirction in the cache and reverse the ids later if ($reverse_ids) diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php index 324c214e91..adaf025730 100644 --- a/phpBB/includes/search/fulltext_mysql.php +++ b/phpBB/includes/search/fulltext_mysql.php @@ -353,7 +353,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No keywords? No posts if (!$this->search_query) @@ -375,6 +375,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base implode(',', $author_ary) ))); + if ($start < 0) + { + $start = 0; + } + // try reading the results from cache $result_count = 0; if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -488,16 +493,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base $id_ary = array_unique($id_ary); - if (!sizeof($id_ary)) - { - return false; - } - // if the total result count is not cached yet, retrieve it from the db if (!$result_count) { - $sql = 'SELECT FOUND_ROWS() as result_count'; - $result = $this->db->sql_query($sql); + $sql_found_rows = 'SELECT FOUND_ROWS() as result_count'; + $result = $this->db->sql_query($sql_found_rows); $result_count = (int) $this->db->sql_fetchfield('result_count'); $this->db->sql_freeresult($result); @@ -507,6 +507,21 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base } } + if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = (int) $row[$field]; + } + $this->db->sql_freeresult($result); + + $id_ary = array_unique($id_ary); + } + // store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page $this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir); $id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -533,7 +548,7 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No author? No posts if (!sizeof($author_ary)) @@ -557,6 +572,11 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base $author_name, ))); + if ($start < 0) + { + $start = 0; + } + // try reading the results from cache $result_count = 0; if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -662,8 +682,8 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base // retrieve the total result count if needed if (!$result_count) { - $sql = 'SELECT FOUND_ROWS() as result_count'; - $result = $this->db->sql_query($sql); + $sql_found_rows = 'SELECT FOUND_ROWS() as result_count'; + $result = $this->db->sql_query($sql_found_rows); $result_count = (int) $this->db->sql_fetchfield('result_count'); $this->db->sql_freeresult($result); @@ -673,6 +693,20 @@ class phpbb_search_fulltext_mysql extends phpbb_search_base } } + if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = (int) $row[$field]; + } + $this->db->sql_freeresult($result); + + $id_ary = array_unique($id_ary); + } + if (sizeof($id_ary)) { $this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php index 53df8348ae..c9f33054fc 100644 --- a/phpBB/includes/search/fulltext_native.php +++ b/phpBB/includes/search/fulltext_native.php @@ -516,7 +516,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No keywords? No posts. if (empty($this->search_query)) @@ -855,10 +855,6 @@ class phpbb_search_fulltext_native extends phpbb_search_base } $this->db->sql_freeresult($result); - if (!sizeof($id_ary)) - { - return false; - } // if we use mysql and the total result count is not cached yet, retrieve it from the db if (!$total_results && $is_mysql) @@ -867,14 +863,14 @@ class phpbb_search_fulltext_native extends phpbb_search_base $sql_array_copy = $sql_array; $sql_array_copy['SELECT'] = 'SQL_CALC_FOUND_ROWS p.post_id '; - $sql = $this->db->sql_build_query('SELECT', $sql_array_copy); + $sql_calc = $this->db->sql_build_query('SELECT', $sql_array_copy); unset($sql_array_copy); - $this->db->sql_query($sql); + $this->db->sql_query($sql_calc); $this->db->sql_freeresult($result); - $sql = 'SELECT FOUND_ROWS() as total_results'; - $result = $this->db->sql_query($sql); + $sql_count = 'SELECT FOUND_ROWS() as total_results'; + $result = $this->db->sql_query($sql_count); $total_results = (int) $this->db->sql_fetchfield('total_results'); $this->db->sql_freeresult($result); @@ -884,6 +880,20 @@ class phpbb_search_fulltext_native extends phpbb_search_base } } + if ($start >= $total_results) + { + $start = floor(($total_results - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = (int) $row[(($type == 'posts') ? 'post_id' : 'topic_id')]; + } + $this->db->sql_freeresult($result); + + } + // store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page $this->save_ids($search_key, $this->search_query, $author_ary, $total_results, $id_ary, $start, $sort_dir); $id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -910,7 +920,7 @@ class phpbb_search_fulltext_native extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No author? No posts if (!sizeof($author_ary)) @@ -1096,13 +1106,13 @@ class phpbb_search_fulltext_native extends phpbb_search_base if (!$total_results && $is_mysql) { // Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it. - $sql = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); + $sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); - $this->db->sql_query($sql); + $this->db->sql_query($sql_calc); $this->db->sql_freeresult($result); - $sql = 'SELECT FOUND_ROWS() as total_results'; - $result = $this->db->sql_query($sql); + $sql_count = 'SELECT FOUND_ROWS() as total_results'; + $result = $this->db->sql_query($sql_count); $total_results = (int) $this->db->sql_fetchfield('total_results'); $this->db->sql_freeresult($result); @@ -1112,6 +1122,19 @@ class phpbb_search_fulltext_native extends phpbb_search_base } } + if ($start >= $total_results) + { + $start = floor(($total_results - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = (int) $row[$field]; + } + $this->db->sql_freeresult($result); + } + if (sizeof($id_ary)) { $this->save_ids($search_key, '', $author_ary, $total_results, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_postgres.php b/phpBB/includes/search/fulltext_postgres.php index 1475cc31d0..eeb628b18f 100644 --- a/phpBB/includes/search/fulltext_postgres.php +++ b/phpBB/includes/search/fulltext_postgres.php @@ -343,7 +343,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No keywords? No posts if (!$this->search_query) @@ -371,6 +371,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base implode(',', $author_ary) ))); + if ($start < 0) + { + $start = 0; + } + // try reading the results from cache $result_count = 0; if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -495,11 +500,6 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base $id_ary = array_unique($id_ary); - if (!sizeof($id_ary)) - { - return false; - } - // if the total result count is not cached yet, retrieve it from the db if (!$result_count) { @@ -518,6 +518,21 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base $this->db->sql_transaction('commit'); + if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = $row[$field]; + } + $this->db->sql_freeresult($result); + + $id_ary = array_unique($id_ary); + } + // store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page $this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir); $id_ary = array_slice($id_ary, 0, (int) $per_page); @@ -544,7 +559,7 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function author_search($type, $firstpost_only, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No author? No posts if (!sizeof($author_ary)) @@ -568,6 +583,11 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base $author_name, ))); + if ($start < 0) + { + $start = 0; + } + // try reading the results from cache $result_count = 0; if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE) @@ -710,6 +730,20 @@ class phpbb_search_fulltext_postgres extends phpbb_search_base $this->db->sql_transaction('commit'); + if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + + $result = $this->db->sql_query_limit($sql, $this->config['search_block_size'], $start); + while ($row = $this->db->sql_fetchrow($result)) + { + $id_ary[] = (int) $row[$field]; + } + $this->db->sql_freeresult($result); + + $id_ary = array_unique($id_ary); + } + if (sizeof($id_ary)) { $this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir); diff --git a/phpBB/includes/search/fulltext_sphinx.php b/phpBB/includes/search/fulltext_sphinx.php index 4bacf74f93..48445d0794 100644 --- a/phpBB/includes/search/fulltext_sphinx.php +++ b/phpBB/includes/search/fulltext_sphinx.php @@ -454,7 +454,7 @@ class phpbb_search_fulltext_sphinx * @param int $per_page number of ids each page is supposed to contain * @return boolean|int total number of results */ - public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, $start, $per_page) + public function keyword_search($type, $fields, $terms, $sort_by_sql, $sort_key, $sort_dir, $sort_days, $ex_fid_ary, $m_approve_fid_ary, $topic_id, $author_ary, $author_name, &$id_ary, &$start, $per_page) { // No keywords? No posts. if (!strlen($this->search_query) && !sizeof($author_ary)) @@ -609,6 +609,25 @@ class phpbb_search_fulltext_sphinx } } + $result_count = $result['total_found']; + + if ($start >= $result_count) + { + $start = floor(($result_count - 1) / $per_page) * $per_page; + + $this->sphinx->SetLimits((int) $start, (int) $per_page, SPHINX_MAX_MATCHES); + $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + + // Could be connection to localhost:9312 failed (errno=111, + // msg=Connection refused) during rotate, retry if so + $retries = SPHINX_CONNECT_RETRIES; + while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--) + { + usleep(SPHINX_CONNECT_WAIT_TIME); + $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + } + } + $id_ary = array(); if (isset($result['matches'])) { @@ -629,8 +648,6 @@ class phpbb_search_fulltext_sphinx return false; } - $result_count = $result['total_found']; - $id_ary = array_slice($id_ary, 0, (int) $per_page); return $result_count; @@ -878,8 +895,8 @@ class phpbb_search_fulltext_sphinx
' . $this->user->lang['MIB'] . '
-

' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
-
' . (($this->config_generate()) ? '' : $this->config_file_data) . '
+

' . $this->user->lang['FULLTEXT_SPHINX_CONFIG_FILE_EXPLAIN'] . '
+
' . (($this->config_generate()) ? '' : $this->config_file_data) . '
'; diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php index ee8a4094c7..6bc71da0c1 100644 --- a/phpBB/includes/session.php +++ b/phpBB/includes/session.php @@ -346,7 +346,7 @@ class phpbb_session $session_id = $request->variable('sid', ''); if (defined('NEED_SID') && (empty($session_id) || $this->session_id !== $session_id)) { - send_status_line(401, 'Not authorized'); + send_status_line(401, 'Unauthorized'); redirect(append_sid("{$phpbb_root_path}index.$phpEx")); } diff --git a/phpBB/install/database_update.php b/phpBB/install/database_update.php index 459839f393..4938ef0f87 100644 --- a/phpBB/install/database_update.php +++ b/phpBB/install/database_update.php @@ -133,9 +133,7 @@ set_config(null, null, null, $config); set_config_count(null, null, null, $config); $orig_version = $config['version']; -include($phpbb_root_path . 'language/' . $config['default_lang'] . '/common.' . $phpEx); -include($phpbb_root_path . 'language/' . $config['default_lang'] . '/acp/common.' . $phpEx); -include($phpbb_root_path . 'language/' . $config['default_lang'] . '/install.' . $phpEx); +$user->add_lang(array('common', 'acp/common', 'install', 'migrator')); // Add own hook handler, if present. :o if (file_exists($phpbb_root_path . 'includes/hooks/index.' . $phpEx)) @@ -157,11 +155,11 @@ else header('Content-type: text/html; charset=UTF-8'); ?> - + -<?php echo $lang['UPDATING_TO_LATEST_STABLE']; ?> +<?php echo $user->lang['UPDATING_TO_LATEST_STABLE']; ?> @@ -178,12 +176,12 @@ header('Content-type: text/html; charset=UTF-8');
-

+

lang['UPDATING_TO_LATEST_STABLE']; ?>


-

:: sql_layer; ?>
- ::
+

lang['DATABASE_TYPE']; ?> :: sql_layer; ?>
+ lang['PREVIOUS_VERSION']; ?> ::
finished()) } catch (phpbb_db_migration_exception $e) { - echo $e; + echo $e->getLocalisedMessage($user); phpbb_end_update($cache); } @@ -236,8 +234,8 @@ while (!$migrator->finished()) if ((time() - $update_start_time) >= $safe_time_limit) { //echo ''; - echo $lang['DATABASE_UPDATE_NOT_COMPLETED'] . '
'; - echo '' . $lang['DATABASE_UPDATE_CONTINUE'] . ''; + echo $user->lang['DATABASE_UPDATE_NOT_COMPLETED'] . '
'; + echo '' . $user->lang['DATABASE_UPDATE_CONTINUE'] . ''; phpbb_end_update($cache); } @@ -248,6 +246,6 @@ if ($orig_version != $config['version']) add_log('admin', 'LOG_UPDATE_DATABASE', $orig_version, $config['version']); } -echo $lang['DATABASE_UPDATE_COMPLETE']; +echo $user->lang['DATABASE_UPDATE_COMPLETE']; phpbb_end_update($cache); diff --git a/phpBB/install/install_install.php b/phpBB/install/install_install.php index a99de9bec9..67649e6b0f 100644 --- a/phpBB/install/install_install.php +++ b/phpBB/install/install_install.php @@ -1457,12 +1457,12 @@ class install_install extends module */ function add_modules($mode, $sub) { - global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager, $config; + global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager, $config, $phpbb_container; // modules require an extension manager if (empty($phpbb_extension_manager)) { - $phpbb_extension_manager = new phpbb_extension_manager($db, $config, EXT_TABLE, $phpbb_root_path, ".$phpEx"); + $phpbb_extension_manager = $phpbb_container->get('ext.manager'); } include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx); diff --git a/phpBB/install/schemas/schema_data.sql b/phpBB/install/schemas/schema_data.sql index 4e4d70ced3..7c1a7d40f5 100644 --- a/phpBB/install/schemas/schema_data.sql +++ b/phpBB/install/schemas/schema_data.sql @@ -611,6 +611,7 @@ INSERT INTO phpbb_acl_groups (group_id, forum_id, auth_option_id, auth_role_id, # NEW MEMBERS on the queue INSERT INTO phpbb_acl_groups (group_id, forum_id, auth_option_id, auth_role_id, auth_setting) VALUES (7, 2, 0, 24, 0); + # -- Demo Topic INSERT INTO phpbb_topics (topic_title, topic_poster, topic_time, topic_views, topic_replies, topic_replies_real, forum_id, topic_status, topic_type, topic_first_post_id, topic_first_poster_name, topic_first_poster_colour, topic_last_post_id, topic_last_poster_id, topic_last_poster_name, topic_last_poster_colour, topic_last_post_subject, topic_last_post_time, topic_last_view_time, poll_title) VALUES ('{L_TOPICS_TOPIC_TITLE}', 2, 972086460, 0, 0, 0, 2, 0, 0, 1, 'Admin', 'AA0000', 1, 2, 'Admin', 'AA0000', '{L_TOPICS_TOPIC_TITLE}', 972086460, 972086460, ''); diff --git a/phpBB/language/en/migrator.php b/phpBB/language/en/migrator.php new file mode 100644 index 0000000000..84074c391c --- /dev/null +++ b/phpBB/language/en/migrator.php @@ -0,0 +1,56 @@ + 'The config setting "%s" unexpectedly already exists.', + 'CONFIG_NOT_EXIST' => 'The config setting "%s" unexpectedly does not exist.', + + 'GROUP_NOT_EXIST' => 'The group "%s" unexpectedly does not exist.', + + 'MIGRATION_EXCEPTION_ERROR' => 'Something went wrong during the request and an exception was thrown. The changes made before the error occurred were reversed to the best of our abilities, but you should check the board for errors.', + 'MIGRATION_NOT_FULFILLABLE' => 'The migration "%1$s" is not fulfillable, missing migration "%2$s".', + + 'MODULE_ALREADY_EXIST' => 'The module "%s" unexpectedly already exists.', + 'MODULE_ERROR' => 'An error occured while creating a module: %s', + 'MODULE_INFO_FILE_NOT_EXIST' => 'A required module info file is missing: %2$s', + 'MODULE_NOT_EXIST' => 'A required module does not exist: %s', + 'MODULE_NOT_REMOVABLE' => 'Module %1$s was unable to be removed: %2$s', + + 'PERMISSION_ALREADY_EXIST' => 'The permission setting "%s" unexpectedly already exists.', + 'PERMISSION_NOT_EXIST' => 'The permission setting "%s" unexpectedly does not exist.', + + 'ROLE_NOT_EXIST' => 'The permission role "%s" unexpectedly does not exist.', +)); diff --git a/tests/extension/manager_test.php b/tests/extension/manager_test.php index 5cde5bccdb..9032afbd73 100644 --- a/tests/extension/manager_test.php +++ b/tests/extension/manager_test.php @@ -25,14 +25,7 @@ class phpbb_extension_manager_test extends phpbb_database_test_case { parent::setUp(); - $this->extension_manager = new phpbb_extension_manager( - $this->new_dbal(), - new phpbb_config(array()), - 'phpbb_ext', - dirname(__FILE__) . '/', - '.php', - new phpbb_mock_cache - ); + $this->extension_manager = $this->create_extension_manager(); } public function test_available() @@ -89,15 +82,30 @@ class phpbb_extension_manager_test extends phpbb_database_test_case public function test_enabled_no_cache() { - $extension_manager = new phpbb_extension_manager( - $this->new_dbal(), - new phpbb_config(array()), - 'phpbb_ext', - dirname(__FILE__) . '/', - '.php' - ); + $extension_manager = $this->create_extension_manager(false); $this->assertEquals(array('foo'), array_keys($extension_manager->all_enabled())); } + protected function create_extension_manager($with_cache = true) + { + + $config = new phpbb_config(array()); + $db = $this->new_dbal(); + $db_tools = new phpbb_db_tools($db); + $phpbb_root_path = __DIR__ . './../../phpBB/'; + $php_ext = 'php'; + $table_prefix = 'phpbb_'; + + return new phpbb_extension_manager( + new phpbb_mock_container_builder(), + $db, + $config, + new phpbb_db_migrator($config, $db, $db_tools, 'phpbb_migrations', $phpbb_root_path, $php_ext, $table_prefix, array()), + 'phpbb_ext', + dirname(__FILE__) . '/', + '.' . $php_ext, + ($with_cache) ? new phpbb_mock_cache() : null + ); + } } diff --git a/tests/extension/metadata_manager_test.php b/tests/extension/metadata_manager_test.php index ce7be0dea5..cdea8d5258 100644 --- a/tests/extension/metadata_manager_test.php +++ b/tests/extension/metadata_manager_test.php @@ -34,9 +34,11 @@ class metadata_manager_test extends phpbb_database_test_case 'version' => '3.1.0', )); $this->db = $this->new_dbal(); + $this->db_tools = new phpbb_db_tools($this->db); $this->phpbb_root_path = dirname(__FILE__) . '/'; $this->phpEx = '.php'; $this->user = new phpbb_user(); + $this->table_prefix = 'phpbb_'; $this->template = new phpbb_template( $this->phpbb_root_path, @@ -48,8 +50,10 @@ class metadata_manager_test extends phpbb_database_test_case ); $this->extension_manager = new phpbb_extension_manager( + new phpbb_mock_container_builder(), $this->db, $this->config, + new phpbb_db_migrator($this->config, $this->db, $this->db_tools, 'phpbb_migrations', $this->phpbb_root_path, $this->php_ext, $this->table_prefix, array()), 'phpbb_ext', $this->phpbb_root_path, $this->phpEx, diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index e346223a4b..b570b464e6 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -134,19 +134,20 @@ class phpbb_functional_test_case extends phpbb_test_case { global $phpbb_root_path, $phpEx; - if (!$this->extension_manager) - { - $this->extension_manager = new phpbb_extension_manager( - $this->get_db(), - new phpbb_config(array()), - self::$config['table_prefix'] . 'ext', - $phpbb_root_path, - ".$phpEx", - $this->get_cache_driver() - ); - } + $config = new phpbb_config(array()); + $db = $this->get_db(); + $db_tools = new phpbb_db_tools($db); - return $this->extension_manager; + return new phpbb_extension_manager( + new phpbb_mock_container_builder(), + $db, + $config, + new phpbb_db_migrator($config, $db, $db_tools, self::$config['table_prefix'] . 'migrations', $phpbb_root_path, $php_ext, self::$config['table_prefix'], array()), + self::$config['table_prefix'] . 'ext', + dirname(__FILE__) . '/', + '.' . $php_ext, + $this->get_cache_driver() + ); } static protected function install_board()