diff options
Diffstat (limited to 'phpBB/phpbb')
68 files changed, 2318 insertions, 509 deletions
diff --git a/phpBB/phpbb/auth/provider/base.php b/phpBB/phpbb/auth/provider/base.php index 4c49070eaf..dea27ccc25 100644 --- a/phpBB/phpbb/auth/provider/base.php +++ b/phpBB/phpbb/auth/provider/base.php @@ -61,7 +61,7 @@ abstract class base implements \phpbb\auth\provider\provider_interface /** * {@inheritdoc} */ - public function get_auth_link_data() + public function get_auth_link_data($user_id = 0) { return; } diff --git a/phpBB/phpbb/auth/provider/ldap.php b/phpBB/phpbb/auth/provider/ldap.php index c71950c698..c48b771ab0 100644 --- a/phpBB/phpbb/auth/provider/ldap.php +++ b/phpBB/phpbb/auth/provider/ldap.php @@ -289,7 +289,6 @@ class ldap extends \phpbb\auth\provider\base /** * {@inheritdoc} */ - public function acp() { // These are fields required in the config table @@ -308,7 +307,7 @@ class ldap extends \phpbb\auth\provider\base 'TEMPLATE_VARS' => array( 'AUTH_LDAP_BASE_DN' => $new_config['ldap_base_dn'], 'AUTH_LDAP_EMAIL' => $new_config['ldap_email'], - 'AUTH_LDAP_PASSORD' => $new_config['ldap_password'], + 'AUTH_LDAP_PASSORD' => $new_config['ldap_password'] !== '' ? '********' : '', 'AUTH_LDAP_PORT' => $new_config['ldap_port'], 'AUTH_LDAP_SERVER' => $new_config['ldap_server'], 'AUTH_LDAP_UID' => $new_config['ldap_uid'], diff --git a/phpBB/phpbb/auth/provider/oauth/oauth.php b/phpBB/phpbb/auth/provider/oauth/oauth.php index c0ce3f1fba..be0fbf5831 100644 --- a/phpBB/phpbb/auth/provider/oauth/oauth.php +++ b/phpBB/phpbb/auth/provider/oauth/oauth.php @@ -553,13 +553,13 @@ class oauth extends \phpbb\auth\provider\base /** * {@inheritdoc} */ - public function get_auth_link_data() + public function get_auth_link_data($user_id = 0) { $block_vars = array(); // Get all external accounts tied to the current user $data = array( - 'user_id' => (int) $this->user->data['user_id'], + 'user_id' => ($user_id <= 0) ? (int) $this->user->data['user_id'] : (int) $user_id, ); $sql = 'SELECT oauth_provider_id, provider FROM ' . $this->auth_provider_oauth_token_account_assoc . ' WHERE ' . $this->db->sql_build_array('SELECT', $data); @@ -616,10 +616,13 @@ class oauth extends \phpbb\auth\provider\base return 'LOGIN_LINK_MISSING_DATA'; } + // Remove user specified in $link_data if possible + $user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id']; + // Remove the link $sql = 'DELETE FROM ' . $this->auth_provider_oauth_token_account_assoc . " WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "' - AND user_id = " . (int) $this->user->data['user_id']; + AND user_id = " . (int) $user_id; $this->db->sql_query($sql); // Clear all tokens belonging to the user on this servce diff --git a/phpBB/phpbb/auth/provider/oauth/token_storage.php b/phpBB/phpbb/auth/provider/oauth/token_storage.php index f488c2022d..9b6afae255 100644 --- a/phpBB/phpbb/auth/provider/oauth/token_storage.php +++ b/phpBB/phpbb/auth/provider/oauth/token_storage.php @@ -13,7 +13,6 @@ namespace phpbb\auth\provider\oauth; - use OAuth\OAuth1\Token\StdOAuth1Token; use OAuth\Common\Token\TokenInterface; use OAuth\Common\Storage\TokenStorageInterface; diff --git a/phpBB/phpbb/auth/provider/provider_interface.php b/phpBB/phpbb/auth/provider/provider_interface.php index 613297cefc..35e0f559a1 100644 --- a/phpBB/phpbb/auth/provider/provider_interface.php +++ b/phpBB/phpbb/auth/provider/provider_interface.php @@ -166,6 +166,10 @@ interface provider_interface /** * Returns an array of data necessary to build the ucp_auth_link page * + * @param int $user_id User ID for whom the data should be retrieved. + * defaults to 0, which is not a valid ID. The method + * should fall back to the current user's ID in this + * case. * @return array|null If this function is not implemented on an auth * provider then it returns null. If it is implemented * it will return an array of up to four elements of @@ -181,7 +185,7 @@ interface provider_interface * 'VARS' => array(...), * ) */ - public function get_auth_link_data(); + public function get_auth_link_data($user_id = 0); /** * Unlinks an external account from a phpBB account. diff --git a/phpBB/phpbb/cache/driver/memcache.php b/phpBB/phpbb/cache/driver/memcache.php index 406ab11ddd..caa82fb0b1 100644 --- a/phpBB/phpbb/cache/driver/memcache.php +++ b/phpBB/phpbb/cache/driver/memcache.php @@ -50,7 +50,7 @@ class memcache extends \phpbb\cache\driver\memory parent::__construct(); $this->memcache = new \Memcache; - foreach(explode(',', PHPBB_ACM_MEMCACHE) as $u) + foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u) { $parts = explode('/', $u); $this->memcache->addServer(trim($parts[0]), trim($parts[1])); diff --git a/phpBB/phpbb/cron/task/core/tidy_search.php b/phpBB/phpbb/cron/task/core/tidy_search.php index ce16b3f988..eb3970254f 100644 --- a/phpBB/phpbb/cron/task/core/tidy_search.php +++ b/phpBB/phpbb/cron/task/core/tidy_search.php @@ -20,24 +20,60 @@ namespace phpbb\cron\task\core; */ class tidy_search extends \phpbb\cron\task\base { + /** + * phpBB root path + * @var string + */ protected $phpbb_root_path; + + /** + * PHP file extension + * @var string + */ protected $php_ext; + + /** + * Auth object + * @var \phpbb\auth\auth + */ protected $auth; + + /** + * Config object + * @var \phpbb\config\config + */ protected $config; + + /** + * Database object + * @var \phpbb\db\driver\driver_interface + */ protected $db; + + /** + * User object + * @var \phpbb\user + */ protected $user; /** + * Event dispatcher object + * @var \phpbb\event\dispatcher_interface + */ + protected $phpbb_dispatcher; + + /** * Constructor. * - * @param string $phpbb_root_path The root path + * @param string $phpbb_root_path The phpBB root path * @param string $php_ext The PHP file extension - * @param \phpbb\auth\auth $auth The auth - * @param \phpbb\config\config $config The config - * @param \phpbb\db\driver\driver_interface $db The db connection - * @param \phpbb\user $user The user + * @param \phpbb\auth\auth $auth The auth object + * @param \phpbb\config\config $config The config object + * @param \phpbb\db\driver\driver_interface $db The database object + * @param \phpbb\user $user The user object + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher The event dispatcher object */ - public function __construct($phpbb_root_path, $php_ext, \phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\user $user) + public function __construct($phpbb_root_path, $php_ext, \phpbb\auth\auth $auth, \phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\user $user, \phpbb\event\dispatcher_interface $phpbb_dispatcher) { $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $php_ext; @@ -45,6 +81,7 @@ class tidy_search extends \phpbb\cron\task\base $this->config = $config; $this->db = $db; $this->user = $user; + $this->phpbb_dispatcher = $phpbb_dispatcher; } /** @@ -58,7 +95,7 @@ class tidy_search extends \phpbb\cron\task\base // We do some additional checks in the module to ensure it can actually be utilised $error = false; - $search = new $search_type($error, $this->phpbb_root_path, $this->php_ext, $this->auth, $this->config, $this->db, $this->user); + $search = new $search_type($error, $this->phpbb_root_path, $this->php_ext, $this->auth, $this->config, $this->db, $this->user, $this->phpbb_dispatcher); if (!$error) { diff --git a/phpBB/phpbb/db/extractor/mssql_extractor.php b/phpBB/phpbb/db/extractor/mssql_extractor.php index d0aa78f1f5..fc30f4789d 100644 --- a/phpBB/phpbb/db/extractor/mssql_extractor.php +++ b/phpBB/phpbb/db/extractor/mssql_extractor.php @@ -184,7 +184,7 @@ class mssql_extractor extends base_extractor { $this->write_data_mssql($table_name); } - else if($this->db->get_sql_layer() === 'mssqlnative') + else if ($this->db->get_sql_layer() === 'mssqlnative') { $this->write_data_mssqlnative($table_name); } diff --git a/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php index 0ca4f2f19c..725c57ca86 100644 --- a/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php +++ b/phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php @@ -13,7 +13,7 @@ namespace phpbb\db\migration\data\v310; -class acp_prune_users_module extends \phpbb\db\migration\migration +class acp_prune_users_module extends \phpbb\db\migration\container_aware_migration { public function effectively_installed() { @@ -70,12 +70,7 @@ class acp_prune_users_module extends \phpbb\db\migration\migration $acp_cat_users_id = (int) $this->db->sql_fetchfield('module_id'); $this->db->sql_freeresult($result); - if (!class_exists('\acp_modules')) - { - include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); - } - $module_manager = new \acp_modules(); - $module_manager->module_class = 'acp'; - $module_manager->move_module($acp_prune_users_id, $acp_cat_users_id); + $module_manager = $this->container->get('module.manager'); + $module_manager->move_module($acp_prune_users_id, $acp_cat_users_id, 'acp'); } } diff --git a/phpBB/phpbb/db/migration/data/v310/dev.php b/phpBB/phpbb/db/migration/data/v310/dev.php index f037191c2a..250258eea7 100644 --- a/phpBB/phpbb/db/migration/data/v310/dev.php +++ b/phpBB/phpbb/db/migration/data/v310/dev.php @@ -13,7 +13,7 @@ namespace phpbb\db\migration\data\v310; -class dev extends \phpbb\db\migration\migration +class dev extends \phpbb\db\migration\container_aware_migration { public function effectively_installed() { @@ -204,18 +204,13 @@ class dev extends \phpbb\db\migration\migration $language_management_module_id = $this->db->sql_fetchfield('module_id'); $this->db->sql_freeresult($result); - if (!class_exists('acp_modules')) - { - include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); - } // acp_modules calls adm_back_link, which is undefined at this point if (!function_exists('adm_back_link')) { include($this->phpbb_root_path . 'includes/functions_acp.' . $this->php_ext); } - $module_manager = new \acp_modules(); - $module_manager->module_class = 'acp'; - $module_manager->move_module($language_module_id, $language_management_module_id); + $module_manager = $this->container->get('module.manager'); + $module_manager->move_module($language_module_id, $language_management_module_id, 'acp'); } public function update_ucp_pm_basename() diff --git a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php index 918a565e06..3b0d53d803 100644 --- a/phpBB/phpbb/db/migration/data/v310/style_update_p1.php +++ b/phpBB/phpbb/db/migration/data/v310/style_update_p1.php @@ -62,8 +62,6 @@ class style_update_p1 extends \phpbb\db\migration\migration public function styles_update() { - global $config; - // Get list of valid 3.1 styles $available_styles = array('prosilver'); @@ -138,7 +136,7 @@ class style_update_p1 extends \phpbb\db\migration\migration if (!sizeof($valid_styles)) { // No valid styles: remove everything and add prosilver - $this->sql_query('DELETE FROM ' . STYLES_TABLE, $errored, $error_ary); + $this->sql_query('DELETE FROM ' . STYLES_TABLE); $sql_ary = array( 'style_name' => 'prosilver', @@ -159,13 +157,13 @@ class style_update_p1 extends \phpbb\db\migration\migration $this->sql_query($sql); $sql = 'SELECT style_id - FROM ' . $table . " + FROM ' . STYLES_TABLE . " WHERE style_name = 'prosilver'"; $result = $this->sql_query($sql); $default_style = $this->db->sql_fetchfield($result); $this->db->sql_freeresult($result); - $config->set('default_style', $default_style); + $this->config->set('default_style', $default_style); $sql = 'UPDATE ' . USERS_TABLE . ' SET user_style = 0'; $this->sql_query($sql); diff --git a/phpBB/phpbb/db/migration/data/v31x/v315.php b/phpBB/phpbb/db/migration/data/v31x/v315.php new file mode 100644 index 0000000000..778cdf717e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v315.php @@ -0,0 +1,31 @@ +<?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\db\migration\data\v31x; + +class v315 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v315rc1', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.1.5')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v315rc1.php b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php new file mode 100644 index 0000000000..4cf4472aa7 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v315rc1.php @@ -0,0 +1,31 @@ +<?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\db\migration\data\v31x; + +class v315rc1 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v314', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.1.5-RC1')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/tool/module.php b/phpBB/phpbb/db/migration/tool/module.php index b6f0372181..69ac71abb7 100644 --- a/phpBB/phpbb/db/migration/tool/module.php +++ b/phpBB/phpbb/db/migration/tool/module.php @@ -13,6 +13,8 @@ namespace phpbb\db\migration\tool; +use phpbb\module\exception\module_exception; + /** * Migration module management tool */ @@ -27,6 +29,9 @@ class module implements \phpbb\db\migration\tool\tool_interface /** @var \phpbb\user */ protected $user; + /** @var \phpbb\module\module_manager */ + protected $module_manager; + /** @var string */ protected $phpbb_root_path; @@ -42,15 +47,17 @@ class module implements \phpbb\db\migration\tool\tool_interface * @param \phpbb\db\driver\driver_interface $db * @param \phpbb\cache\service $cache * @param \phpbb\user $user + * @param \phpbb\module\module_manager $module_manager * @param string $phpbb_root_path * @param string $php_ext * @param string $modules_table */ - public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, $phpbb_root_path, $php_ext, $modules_table) + public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\user $user, \phpbb\module\module_manager $module_manager, $phpbb_root_path, $php_ext, $modules_table) { $this->db = $db; $this->cache = $cache; $this->user = $user; + $this->module_manager = $module_manager; $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $php_ext; $this->modules_table = $modules_table; @@ -188,7 +195,6 @@ class module implements \phpbb\db\migration\tool\tool_interface $basename = (isset($data['module_basename'])) ? $data['module_basename'] : ''; $module = $this->get_module_info($class, $basename); - $result = ''; foreach ($module['modes'] as $mode => $module_info) { if (!isset($data['modes']) || in_array($mode, $data['modes'])) @@ -239,13 +245,6 @@ class module implements \phpbb\db\migration\tool\tool_interface return; } - if (!class_exists('acp_modules')) - { - include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); - $this->user->add_lang('acp/modules'); - } - $acp_modules = new \acp_modules(); - $module_data = array( 'module_enabled' => (isset($data['module_enabled'])) ? $data['module_enabled'] : 1, 'module_display' => (isset($data['module_display'])) ? $data['module_display'] : 1, @@ -256,16 +255,11 @@ class module implements \phpbb\db\migration\tool\tool_interface 'module_mode' => (isset($data['module_mode'])) ? $data['module_mode'] : '', 'module_auth' => (isset($data['module_auth'])) ? $data['module_auth'] : '', ); - $result = $acp_modules->update_module_data($module_data, true); - // update_module_data can either return a string or an empty array... - if (is_string($result)) - { - // Error - throw new \phpbb\db\migration\exception('MODULE_ERROR', $result); - } - else + try { + $this->module_manager->update_module_data($module_data); + // Success $module_log_name = ((isset($this->user->lang[$data['module_langname']])) ? $this->user->lang[$data['module_langname']] : $data['module_langname']); $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_MODULE_ADD', false, array($module_log_name)); @@ -318,6 +312,11 @@ class module implements \phpbb\db\migration\tool\tool_interface $this->db->sql_query($sql); } } + catch (module_exception $e) + { + // Error + throw new \phpbb\db\migration\exception('MODULE_ERROR', $e->getMessage()); + } // Clear the Modules Cache $this->cache->destroy("_modules_$class"); @@ -417,21 +416,9 @@ class module implements \phpbb\db\migration\tool\tool_interface $module_ids[] = (int) $module; } - if (!class_exists('acp_modules')) - { - include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); - $this->user->add_lang('acp/modules'); - } - $acp_modules = new \acp_modules(); - $acp_modules->module_class = $class; - foreach ($module_ids as $module_id) { - $result = $acp_modules->delete_module($module_id); - if (!empty($result)) - { - return; - } + $this->module_manager->delete_module($module_id, $class); } $this->cache->destroy("_modules_$class"); @@ -474,13 +461,7 @@ class module implements \phpbb\db\migration\tool\tool_interface */ protected function get_module_info($class, $basename) { - if (!class_exists('acp_modules')) - { - include($this->phpbb_root_path . 'includes/acp/acp_modules.' . $this->php_ext); - $this->user->add_lang('acp/modules'); - } - $acp_modules = new \acp_modules(); - $module = $acp_modules->get_module_infos($basename, $class, true); + $module = $this->module_manager->get_module_infos($class, $basename, true); if (empty($module)) { diff --git a/phpBB/phpbb/db/migration/tool/permission.php b/phpBB/phpbb/db/migration/tool/permission.php index 1a91127d2d..ceff6d7d5a 100644 --- a/phpBB/phpbb/db/migration/tool/permission.php +++ b/phpBB/phpbb/db/migration/tool/permission.php @@ -425,13 +425,27 @@ class permission implements \phpbb\db\migration\tool\tool_interface $role_id = (int) $this->db->sql_fetchfield('auth_role_id'); if ($role_id) { - $sql = 'SELECT role_name + $sql = 'SELECT role_name, role_type FROM ' . ACL_ROLES_TABLE . ' WHERE role_id = ' . $role_id; $this->db->sql_query($sql); - $role_name = $this->db->sql_fetchfield('role_name'); - - return $this->permission_set($role_name, $auth_option, 'role', $has_permission); + $role_data = $this->db->sql_fetchrow(); + $role_name = $role_data['role_name']; + $role_type = $role_data['role_type']; + + // Filter new auth options to match the role type: a_ | f_ | m_ | u_ + // Set new auth options to the role only if options matching the role type were found + $auth_option = array_filter($auth_option, + function ($option) use ($role_type) + { + return strpos($option, $role_type) === 0; + } + ); + + if (sizeof($auth_option)) + { + return $this->permission_set($role_name, $auth_option, 'role', $has_permission); + } } $sql = 'SELECT auth_option_id, auth_setting diff --git a/phpBB/phpbb/db/sql_insert_buffer.php b/phpBB/phpbb/db/sql_insert_buffer.php index 14e3c54f09..18e4814a77 100644 --- a/phpBB/phpbb/db/sql_insert_buffer.php +++ b/phpBB/phpbb/db/sql_insert_buffer.php @@ -107,7 +107,7 @@ class sql_insert_buffer * first building a huge rowset. Or at least sizeof($rows) should be kept * small. * - * @param array $rows + * @param array $rows * * @return bool True when some data was flushed to the database. * False otherwise. diff --git a/phpBB/phpbb/di/container_builder.php b/phpBB/phpbb/di/container_builder.php index 99576f9020..c9adbe7d63 100644 --- a/phpBB/phpbb/di/container_builder.php +++ b/phpBB/phpbb/di/container_builder.php @@ -13,6 +13,7 @@ namespace phpbb\di; +use phpbb\filesystem\filesystem; use Symfony\Component\Config\ConfigCache; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -20,11 +21,17 @@ use Symfony\Component\DependencyInjection\Dumper\PhpDumper; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; +use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; class container_builder { /** + * @var string The environment to use. + */ + protected $environment; + + /** * @var string phpBB Root Path */ protected $phpbb_root_path; @@ -35,89 +42,58 @@ class container_builder protected $php_ext; /** - * The container under construction - * - * @var ContainerBuilder - */ + * The container under construction + * + * @var ContainerBuilder + */ protected $container; /** - * @var \phpbb\db\driver\driver_interface - */ - protected $dbal_connection = null; - - /** - * @var array the installed extensions - */ - protected $installed_exts = null; - - /** - * Indicates whether the php config file should be injected into the container (default to true). - * - * @var bool - */ - protected $inject_config = true; - - /** - * Indicates whether extensions should be used (default to true). - * - * @var bool - */ + * Indicates whether extensions should be used (default to true). + * + * @var bool + */ protected $use_extensions = true; /** - * Defines a custom path to find the configuration of the container (default to $this->phpbb_root_path . 'config') - * - * @var string - */ + * Defines a custom path to find the configuration of the container (default to $this->phpbb_root_path . 'config') + * + * @var string + */ protected $config_path = null; /** - * Indicates whether the phpBB compile pass should be used (default to true). - * - * @var bool - */ - protected $use_custom_pass = true; - - /** - * Indicates whether the kernel compile pass should be used (default to true). - * - * @var bool - */ - protected $use_kernel_pass = true; - - /** - * Indicates whether the container should be dumped to the filesystem (default to true). - * - * If DEBUG_CONTAINER is set this option is ignored and a new container is build. - * - * @var bool - */ - protected $dump_container = true; + * Indicates whether the container should be dumped to the filesystem (default to true). + * + * If DEBUG_CONTAINER is set this option is ignored and a new container is build. + * + * @var bool + */ + protected $use_cache = true; /** - * Indicates if the container should be compiled automatically (default to true). - * - * @var bool - */ + * Indicates if the container should be compiled automatically (default to true). + * + * @var bool + */ protected $compile_container = true; /** - * Custom parameters to inject into the container. - * - * Default to true: - * array( - * 'core.root_path', $this->phpbb_root_path, - * 'core.php_ext', $this->php_ext, - * ); - * - * @var array - */ + * Custom parameters to inject into the container. + * + * Default to: + * array( + * 'core.root_path', $this->phpbb_root_path, + * 'core.php_ext', $this->php_ext, + * ); + * + * @var array + */ protected $custom_parameters = null; /** - * @var \phpbb\config_php_file - */ + * @var \phpbb\config_php_file + */ protected $config_php_file; /** @@ -126,74 +102,64 @@ class container_builder protected $cache_dir; /** - * Constructor - * - * @param \phpbb\config_php_file $config_php_file - * @param string $phpbb_root_path Path to the phpbb includes directory. - * @param string $php_ext php file extension - */ - function __construct(\phpbb\config_php_file $config_php_file, $phpbb_root_path, $php_ext) + * @var array + */ + private $container_extensions; + + /** + * Constructor + * + * @param string $phpbb_root_path Path to the phpbb includes directory. + * @param string $php_ext php file extension + */ + function __construct($phpbb_root_path, $php_ext) { - $this->config_php_file = $config_php_file; $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $php_ext; } /** - * Build and return a new Container respecting the current configuration - * - * @return \phpbb_cache_container|ContainerBuilder - */ + * Build and return a new Container respecting the current configuration + * + * @return \phpbb_cache_container|ContainerBuilder + */ public function get_container() { $container_filename = $this->get_container_filename(); $config_cache = new ConfigCache($container_filename, defined('DEBUG')); - if ($this->dump_container && $config_cache->isFresh()) + if ($this->use_cache && $config_cache->isFresh()) { require($config_cache->getPath()); $this->container = new \phpbb_cache_container(); } else { - $container_extensions = array(new \phpbb\di\extension\core($this->get_config_path())); + $this->container_extensions = array(new extension\core($this->get_config_path())); if ($this->use_extensions) { - $installed_exts = $this->get_installed_extensions(); - foreach ($installed_exts as $ext_name => $path) - { - $extension_class = '\\' . str_replace('/', '\\', $ext_name) . '\\di\\extension'; - - if (!class_exists($extension_class)) - { - $extension_class = '\phpbb\extension\di\extension_base'; - } - - $container_extensions[] = new $extension_class($ext_name, $path); - } + $this->load_extensions(); } - if ($this->inject_config) + // Inject the config + if ($this->config_php_file) { - $container_extensions[] = new \phpbb\di\extension\config($this->config_php_file); + $this->container_extensions[] = new extension\config($this->config_php_file); } - $this->container = $this->create_container($container_extensions); + $this->container = $this->create_container($this->container_extensions); - if ($this->use_custom_pass) - { - // Symfony Kernel Listeners - $this->container->addCompilerPass(new \phpbb\di\pass\collection_pass()); - $this->container->addCompilerPass(new RegisterListenersPass('dispatcher', 'event.listener_listener', 'event.listener')); + // Easy collections through tags + $this->container->addCompilerPass(new pass\collection_pass()); - if ($this->use_kernel_pass) - { - $this->container->addCompilerPass(new RegisterListenersPass('dispatcher')); - } - } + // Event listeners "phpBB style" + $this->container->addCompilerPass(new RegisterListenersPass('dispatcher', 'event.listener_listener', 'event.listener')); - $filesystem = new \phpbb\filesystem\filesystem(); - $loader = new YamlFileLoader($this->container, new FileLocator($filesystem->realpath($this->get_config_path()))); + // Event listeners "Symfony style" + $this->container->addCompilerPass(new RegisterListenersPass('dispatcher')); + + $filesystem = new filesystem(); + $loader = new YamlFileLoader($this->container, new FileLocator($filesystem->realpath($this->get_config_path()))); $loader->load($this->container->getParameter('core.environment') . '/config.yml'); $this->inject_custom_parameters(); @@ -201,124 +167,169 @@ class container_builder if ($this->compile_container) { $this->container->compile(); - } - if ($this->dump_container) - { - $this->dump_container($config_cache); + if ($this->use_cache) + { + $this->dump_container($config_cache); + } } } - $this->container->set('config.php', $this->config_php_file); - - if ($this->compile_container) + if ($this->compile_container && $this->config_php_file) { - $this->inject_dbal(); + $this->container->set('config.php', $this->config_php_file); } return $this->container; } /** - * Set if the extensions should be used. - * - * @param bool $use_extensions - */ - public function set_use_extensions($use_extensions) + * Enable the extensions. + * + * @param string $environment The environment to use + * @return $this + */ + public function with_environment($environment) + { + $this->environment = $environment; + + return $this; + } + + /** + * Enable the extensions. + * + * @return $this + */ + public function with_extensions() { - $this->use_extensions = $use_extensions; + $this->use_extensions = true; + + return $this; + } + + /** + * Disable the extensions. + * + * @return $this + */ + public function without_extensions() + { + $this->use_extensions = false; + + return $this; } /** - * Set if the phpBB compile pass have to be used. - * - * @param bool $use_custom_pass - */ - public function set_use_custom_pass($use_custom_pass) + * Enable the caching of the container. + * + * If DEBUG_CONTAINER is set this option is ignored and a new container is build. + * + * @return $this + */ + public function with_cache() { - $this->use_custom_pass = $use_custom_pass; + $this->use_cache = true; + + return $this; } /** - * Set if the kernel compile pass have to be used. - * - * @param bool $use_kernel_pass - */ - public function set_use_kernel_pass($use_kernel_pass) + * Disable the caching of the container. + * + * @return $this + */ + public function without_cache() { - $this->use_kernel_pass = $use_kernel_pass; + $this->use_cache = false; + + return $this; } /** - * Set if the php config file should be injecting into the container. - * - * @param bool $inject_config - */ - public function set_inject_config($inject_config) + * Set the cache directory. + * + * @param string $cache_dir The cache directory. + * @return $this + */ + public function with_cache_dir($cache_dir) { - $this->inject_config = $inject_config; + $this->cache_dir = $cache_dir; + + return $this; } /** - * Set if a dump container should be used. - * - * If DEBUG_CONTAINER is set this option is ignored and a new container is build. - * - * @var bool $dump_container - */ - public function set_dump_container($dump_container) + * Enable the compilation of the container. + * + * @return $this + */ + public function with_compiled_container() { - $this->dump_container = $dump_container; + $this->compile_container = true; + + return $this; } /** - * Set if the container should be compiled automatically (default to true). - * - * @var bool $dump_container - */ - public function set_compile_container($compile_container) + * Disable the compilation of the container. + * + * @return $this + */ + public function without_compiled_container() { - $this->compile_container = $compile_container; + $this->compile_container = false; + + return $this; } /** - * Set a custom path to find the configuration of the container - * - * @param string $config_path - */ - public function set_config_path($config_path) + * Set a custom path to find the configuration of the container. + * + * @param string $config_path + * @return $this + */ + public function with_config_path($config_path) { $this->config_path = $config_path; + + return $this; } /** - * Returns the path to the container configuration (default: root_path/config) + * Set custom parameters to inject into the container. * - * @return string + * @param array $custom_parameters + * @return $this */ - protected function get_config_path() + public function with_custom_parameters($custom_parameters) { - return $this->config_path ?: $this->phpbb_root_path . 'config'; + $this->custom_parameters = $custom_parameters; + + return $this; } /** - * Set custom parameters to inject into the container. - * - * @param array $custom_parameters - */ - public function set_custom_parameters($custom_parameters) + * Set custom parameters to inject into the container. + * + * @param \phpbb\config_php_file $config_php_file + * @return $this + */ + public function with_config(\phpbb\config_php_file $config_php_file) { - $this->custom_parameters = $custom_parameters; + $this->config_php_file = $config_php_file; + + return $this; } /** - * Set the path to the cache directory. + * Returns the path to the container configuration (default: root_path/config) * - * @param string $cache_dir Path to the cache directory + * @return string */ - public function set_cache_dir($cache_dir) + protected function get_config_path() { - $this->cache_dir = $cache_dir; + return $this->config_path ?: $this->phpbb_root_path . 'config'; } /** @@ -332,89 +343,85 @@ class container_builder } /** - * Dump the container to the disk. - * - * @param ConfigCache $cache The config cache - */ - protected function dump_container($cache) + * Load the enabled extensions. + */ + protected function load_extensions() { - $dumper = new PhpDumper($this->container); - $cached_container_dump = $dumper->dump(array( - 'class' => 'phpbb_cache_container', - 'base_class' => 'Symfony\\Component\\DependencyInjection\\ContainerBuilder', - )); + if ($this->config_php_file !== null) + { + // Build an intermediate container to load the ext list from the database + $container_builder = new container_builder($this->phpbb_root_path, $this->php_ext); + $ext_container = $container_builder + ->without_cache() + ->without_extensions() + ->with_config($this->config_php_file) + ->with_environment('production') + ->without_compiled_container() + ->get_container() + ; + + $ext_container->register('cache.driver', '\\phpbb\\cache\\driver\\dummy'); + $ext_container->compile(); + + $extensions = $ext_container->get('ext.manager')->all_enabled(); + + // Load each extension found + foreach ($extensions as $ext_name => $path) + { + $extension_class = '\\' . str_replace('/', '\\', $ext_name) . '\\di\\extension'; - $cache->write($cached_container_dump, $this->container->getResources()); - } + if (!class_exists($extension_class)) + { + $extension_class = '\\phpbb\\extension\\di\\extension_base'; + } - /** - * Inject the connection into the container if one was opened. - */ - protected function inject_dbal() - { - if ($this->dbal_connection !== null) - { - $this->container->get('dbal.conn')->set_driver($this->dbal_connection); - } - } + $this->container_extensions[] = new $extension_class($ext_name, $path); - /** - * Get DB connection. - * - * @return \phpbb\db\driver\driver_interface - */ - protected function get_dbal_connection() - { - if ($this->dbal_connection === null) + // Load extension autoloader + $filename = $path . 'vendor/autoload.php'; + if (file_exists($filename)) + { + require $filename; + } + } + } + else { - $dbal_driver_class = $this->config_php_file->convert_30_dbms_to_31($this->config_php_file->get('dbms')); - $this->dbal_connection = new $dbal_driver_class(); - $this->dbal_connection->sql_connect( - $this->config_php_file->get('dbhost'), - $this->config_php_file->get('dbuser'), - $this->config_php_file->get('dbpasswd'), - $this->config_php_file->get('dbname'), - $this->config_php_file->get('dbport'), - defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK - ); + // To load the extensions we need the database credentials. + // Automatically disable the extensions if we don't have them. + $this->use_extensions = false; } - - return $this->dbal_connection; } /** - * Get enabled extensions. - * - * @return array enabled extensions - */ - protected function get_installed_extensions() + * Dump the container to the disk. + * + * @param ConfigCache $cache The config cache + */ + protected function dump_container($cache) { - $db = $this->get_dbal_connection(); - $extension_table = $this->config_php_file->get('table_prefix') . 'ext'; - - $sql = 'SELECT * - FROM ' . $extension_table . ' - WHERE ext_active = 1'; - - $result = $db->sql_query($sql); - $rows = $db->sql_fetchrowset($result); - $db->sql_freeresult($result); + try + { + $dumper = new PhpDumper($this->container); + $cached_container_dump = $dumper->dump(array( + 'class' => 'phpbb_cache_container', + 'base_class' => 'Symfony\\Component\\DependencyInjection\\ContainerBuilder', + )); - $exts = array(); - foreach ($rows as $row) + $cache->write($cached_container_dump, $this->container->getResources()); + } + catch (IOException $e) { - $exts[$row['ext_name']] = $this->phpbb_root_path . 'ext/' . $row['ext_name'] . '/'; + // Don't fail if the cache isn't writeable } - - return $exts; } /** - * Create the ContainerBuilder object - * - * @param array $extensions Array of Container extension objects - * @return ContainerBuilder object - */ + * Create the ContainerBuilder object + * + * @param array $extensions Array of Container extension objects + * @return ContainerBuilder object + */ protected function create_container(array $extensions) { $container = new ContainerBuilder(new ParameterBag($this->get_core_parameters())); @@ -425,7 +432,6 @@ class container_builder { $container->registerExtension($extension); $extensions_alias[] = $extension->getAlias(); - //$container->loadFromExtension($extension->getAlias()); } $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions_alias)); @@ -487,14 +493,13 @@ class container_builder } /** - * Get the filename under which the dumped container will be stored. - * - * @return string Path for dumped container - */ + * Get the filename under which the dumped container will be stored. + * + * @return string Path for dumped container + */ protected function get_container_filename() { - $filename = str_replace(array('/', '.'), array('slash', 'dot'), $this->phpbb_root_path); - return $this->get_cache_dir() . 'container_' . $filename . '.' . $this->php_ext; + return $this->get_cache_dir() . 'container_' . md5($this->phpbb_root_path) . '.' . $this->php_ext; } /** @@ -504,6 +509,6 @@ class container_builder */ protected function get_environment() { - return PHPBB_ENVIRONMENT; + return $this->environment ?: PHPBB_ENVIRONMENT; } } diff --git a/phpBB/phpbb/di/extension/core.php b/phpBB/phpbb/di/extension/core.php index c9e2d4dc5b..91b321a684 100644 --- a/phpBB/phpbb/di/extension/core.php +++ b/phpBB/phpbb/di/extension/core.php @@ -71,7 +71,7 @@ class core extends Extension // Set the Twig options if defined in the environment $definition = $container->getDefinition('template.twig.environment'); - $twig_environment_options = $definition->getArgument(6); + $twig_environment_options = $definition->getArgument(7); if ($config['twig']['debug']) { $twig_environment_options['debug'] = true; @@ -80,8 +80,8 @@ class core extends Extension { $twig_environment_options['auto_reload'] = true; } - // Replace the 6th argument, the options passed to the environment - $definition->replaceArgument(6, $twig_environment_options); + // Replace the 8th argument, the options passed to the environment + $definition->replaceArgument(7, $twig_environment_options); if ($config['twig']['enable_debug_extension']) { diff --git a/phpBB/phpbb/event/md_exporter.php b/phpBB/phpbb/event/md_exporter.php index 05e898a157..e042d0a5d1 100644 --- a/phpBB/phpbb/event/md_exporter.php +++ b/phpBB/phpbb/event/md_exporter.php @@ -157,20 +157,64 @@ class md_exporter } list($file_details, $details) = explode("\n* Since: ", $details, 2); - list($since, $description) = explode("\n* Purpose: ", $details, 2); + + $changed_versions = array(); + if (strpos($details, "\n* Changed: ") !== false) + { + list($since, $details) = explode("\n* Changed: ", $details, 2); + while (strpos($details, "\n* Changed: ") !== false) + { + list($changed, $details) = explode("\n* Changed: ", $details, 2); + $changed_versions[] = $changed; + } + list($changed, $description) = explode("\n* Purpose: ", $details, 2); + $changed_versions[] = $changed; + } + else + { + list($since, $description) = explode("\n* Purpose: ", $details, 2); + $changed_versions = array(); + } $files = $this->validate_file_list($file_details); $since = $this->validate_since($since); + $changes = array(); + foreach ($changed_versions as $changed) + { + list($changed_version, $changed_description) = $this->validate_changed($changed); + + if (isset($changes[$changed_version])) + { + throw new \LogicException("Duplicate change information found for event '{$this->current_event}'"); + } + + $changes[$changed_version] = $changed_description; + } + $description = trim($description, "\n") . "\n"; if (!$this->version_is_filtered($since)) { - continue; + $is_filtered = false; + foreach ($changes as $version => $null) + { + if ($this->version_is_filtered($version)) + { + $is_filtered = true; + break; + } + } + + if (!$is_filtered) + { + continue; + } } $this->events[$event_name] = array( 'event' => $this->current_event, 'files' => $files, 'since' => $since, + 'changed' => $changes, 'description' => $description, ); } @@ -182,6 +226,7 @@ class md_exporter * The version to check * * @param string $version + * @return bool */ protected function version_is_filtered($version) { @@ -269,7 +314,7 @@ class md_exporter */ public function validate_since($since) { - if (!preg_match('#^\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?$#', $since)) + if (!$this->validate_version($since)) { throw new \LogicException("Invalid since information found for event '{$this->current_event}'"); } @@ -278,6 +323,44 @@ class md_exporter } /** + * Validate "Changed" Information + * + * @param string $changed + * @return string + * @throws \LogicException + */ + public function validate_changed($changed) + { + if (strpos($changed, ' ') !== false) + { + list($version, $description) = explode(' ', $changed, 2); + } + else + { + $version = $changed; + $description = ''; + } + + if (!$this->validate_version($version)) + { + throw new \LogicException("Invalid changed information found for event '{$this->current_event}'"); + } + + return array($version, $description); + } + + /** + * Validate "version" Information + * + * @param string $version + * @return bool True if valid, false otherwise + */ + public function validate_version($version) + { + return preg_match('#^\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?$#', $version); + } + + /** * Validate the files list * * @param string $file_details diff --git a/phpBB/phpbb/event/php_exporter.php b/phpBB/phpbb/event/php_exporter.php index 8cffa4620f..d2ab0595c0 100644 --- a/phpBB/phpbb/event/php_exporter.php +++ b/phpBB/phpbb/event/php_exporter.php @@ -293,6 +293,7 @@ class php_exporter * The version to check * * @param string $version + * @return bool */ protected function version_is_filtered($version) { diff --git a/phpBB/phpbb/help/controller/bbcode.php b/phpBB/phpbb/help/controller/bbcode.php new file mode 100644 index 0000000000..e16f99023d --- /dev/null +++ b/phpBB/phpbb/help/controller/bbcode.php @@ -0,0 +1,85 @@ +<?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\help\controller; + +/** + * BBCode help page + */ +class bbcode extends controller +{ + /** + * @return string The title of the page + */ + public function display() + { + $this->language->add_lang('help/bbcode'); + + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_INTRO', + false, + array( + 'HELP_BBCODE_INTRO_BBCODE_QUESTION' => 'HELP_BBCODE_INTRO_BBCODE_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_TEXT', + false, + array( + 'HELP_BBCODE_TEXT_BASIC_QUESTION' => 'HELP_BBCODE_TEXT_BASIC_ANSWER', + 'HELP_BBCODE_TEXT_COLOR_QUESTION' => 'HELP_BBCODE_TEXT_COLOR_ANSWER', + 'HELP_BBCODE_TEXT_COMBINE_QUESTION' => 'HELP_BBCODE_TEXT_COMBINE_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_QUOTES', + false, + array( + 'HELP_BBCODE_QUOTES_TEXT_QUESTION' => 'HELP_BBCODE_QUOTES_TEXT_ANSWER', + 'HELP_BBCODE_QUOTES_CODE_QUESTION' => 'HELP_BBCODE_QUOTES_CODE_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_LISTS', + false, + array( + 'HELP_BBCODE_LISTS_UNORDERER_QUESTION' => 'HELP_BBCODE_LISTS_UNORDERER_ANSWER', + 'HELP_BBCODE_LISTS_ORDERER_QUESTION' => 'HELP_BBCODE_LISTS_ORDERER_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_LINKS', + true, + array( + 'HELP_BBCODE_LINKS_BASIC_QUESTION' => 'HELP_BBCODE_LINKS_BASIC_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_IMAGES', + false, + array( + 'HELP_BBCODE_IMAGES_BASIC_QUESTION' => 'HELP_BBCODE_IMAGES_BASIC_ANSWER', + 'HELP_BBCODE_IMAGES_ATTACHMENT_QUESTION' => 'HELP_BBCODE_IMAGES_ATTACHMENT_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_BBCODE_BLOCK_OTHERS', + false, + array( + 'HELP_BBCODE_OTHERS_CUSTOM_QUESTION' => 'HELP_BBCODE_OTHERS_CUSTOM_ANSWER', + ) + ); + + return $this->language->lang('BBCODE_GUIDE'); + } +} diff --git a/phpBB/phpbb/help/controller/controller.php b/phpBB/phpbb/help/controller/controller.php new file mode 100644 index 0000000000..29494205a9 --- /dev/null +++ b/phpBB/phpbb/help/controller/controller.php @@ -0,0 +1,76 @@ +<?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\help\controller; + +/** + * BBCode help page + */ +abstract class controller +{ + /** @var \phpbb\controller\helper */ + protected $helper; + + /** @var \phpbb\help\manager */ + protected $manager; + + /** @var \phpbb\template\template */ + protected $template; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var string */ + protected $root_path; + + /** @var string */ + protected $php_ext; + + /** + * Constructor + * + * @param \phpbb\controller\helper $helper + * @param \phpbb\help\manager $manager + * @param \phpbb\template\template $template + * @param \phpbb\language\language $language + * @param string $root_path + * @param string $php_ext + */ + public function __construct(\phpbb\controller\helper $helper, \phpbb\help\manager $manager, \phpbb\template\template $template, \phpbb\language\language $language, $root_path, $php_ext) + { + $this->helper = $helper; + $this->manager = $manager; + $this->template = $template; + $this->language = $language; + $this->root_path = $root_path; + $this->php_ext = $php_ext; + } + + /** + * @return string + */ + abstract protected function display(); + + public function handle() + { + $title = $this->display(); + + $this->template->assign_vars(array( + 'L_FAQ_TITLE' => $title, + 'S_IN_FAQ' => true, + )); + + make_jumpbox(append_sid("{$this->root_path}viewforum.{$this->php_ext}")); + return $this->helper->render('faq_body.html', $title); + } +} diff --git a/phpBB/phpbb/help/controller/faq.php b/phpBB/phpbb/help/controller/faq.php new file mode 100644 index 0000000000..5e45cfe667 --- /dev/null +++ b/phpBB/phpbb/help/controller/faq.php @@ -0,0 +1,165 @@ +<?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\help\controller; + +/** + * FAQ help page + */ +class faq extends controller +{ + /** + * @return string The title of the page + */ + public function display() + { + $this->language->add_lang('help/faq'); + + $this->manager->add_block( + 'HELP_FAQ_BLOCK_LOGIN', + false, + array( + 'HELP_FAQ_LOGIN_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_ANSWER', + 'HELP_FAQ_LOGIN_COPPA_QUESTION' => 'HELP_FAQ_LOGIN_COPPA_ANSWER', + 'HELP_FAQ_LOGIN_CANNOT_REGISTER_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_REGISTER_ANSWER', + 'HELP_FAQ_LOGIN_REGISTER_CONFIRM_QUESTION' => 'HELP_FAQ_LOGIN_REGISTER_CONFIRM_ANSWER', + 'HELP_FAQ_LOGIN_CANNOT_LOGIN_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANSWER', + 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_QUESTION' => 'HELP_FAQ_LOGIN_CANNOT_LOGIN_ANYMORE_ANSWER', + 'HELP_FAQ_LOGIN_LOST_PASSWORD_QUESTION' => 'HELP_FAQ_LOGIN_LOST_PASSWORD_ANSWER', + 'HELP_FAQ_LOGIN_AUTO_LOGOUT_QUESTION' => 'HELP_FAQ_LOGIN_AUTO_LOGOUT_ANSWER', + 'HELP_FAQ_LOGIN_DELETE_COOKIES_QUESTION' => 'HELP_FAQ_LOGIN_DELETE_COOKIES_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_USERSETTINGS', + false, + array( + 'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_QUESTION' => 'HELP_FAQ_USERSETTINGS_CHANGE_SETTINGS_ANSWER', + 'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_QUESTION' => 'HELP_FAQ_USERSETTINGS_HIDE_ONLINE_ANSWER', + 'HELP_FAQ_USERSETTINGS_TIMEZONE_QUESTION' => 'HELP_FAQ_USERSETTINGS_TIMEZONE_ANSWER', + 'HELP_FAQ_USERSETTINGS_SERVERTIME_QUESTION' => 'HELP_FAQ_USERSETTINGS_SERVERTIME_ANSWER', + 'HELP_FAQ_USERSETTINGS_LANGUAGE_QUESTION' => 'HELP_FAQ_USERSETTINGS_LANGUAGE_ANSWER', + 'HELP_FAQ_USERSETTINGS_AVATAR_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_ANSWER', + 'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_QUESTION' => 'HELP_FAQ_USERSETTINGS_AVATAR_DISPLAY_ANSWER', + 'HELP_FAQ_USERSETTINGS_RANK_QUESTION' => 'HELP_FAQ_USERSETTINGS_RANK_ANSWER', + 'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_QUESTION' => 'HELP_FAQ_USERSETTINGS_EMAIL_LOGIN_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_POSTING', + false, + array( + 'HELP_FAQ_POSTING_CREATE_QUESTION' => 'HELP_FAQ_POSTING_CREATE_ANSWER', + 'HELP_FAQ_POSTING_EDIT_DELETE_QUESTION' => 'HELP_FAQ_POSTING_EDIT_DELETE_ANSWER', + 'HELP_FAQ_POSTING_SIGNATURE_QUESTION' => 'HELP_FAQ_POSTING_SIGNATURE_ANSWER', + 'HELP_FAQ_POSTING_POLL_CREATE_QUESTION' => 'HELP_FAQ_POSTING_POLL_CREATE_ANSWER', + 'HELP_FAQ_POSTING_POLL_ADD_QUESTION' => 'HELP_FAQ_POSTING_POLL_ADD_ANSWER', + 'HELP_FAQ_POSTING_POLL_EDIT_QUESTION' => 'HELP_FAQ_POSTING_POLL_EDIT_ANSWER', + 'HELP_FAQ_POSTING_FORUM_RESTRICTED_QUESTION' => 'HELP_FAQ_POSTING_FORUM_RESTRICTED_ANSWER', + 'HELP_FAQ_POSTING_NO_ATTACHMENTS_QUESTION' => 'HELP_FAQ_POSTING_NO_ATTACHMENTS_ANSWER', + 'HELP_FAQ_POSTING_WARNING_QUESTION' => 'HELP_FAQ_POSTING_WARNING_ANSWER', + 'HELP_FAQ_POSTING_REPORT_QUESTION' => 'HELP_FAQ_POSTING_REPORT_ANSWER', + 'HELP_FAQ_POSTING_DRAFT_QUESTION' => 'HELP_FAQ_POSTING_DRAFT_ANSWER', + 'HELP_FAQ_POSTING_QUEUE_QUESTION' => 'HELP_FAQ_POSTING_QUEUE_ANSWER', + 'HELP_FAQ_POSTING_BUMP_QUESTION' => 'HELP_FAQ_POSTING_BUMP_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_FORMATTING', + false, + array( + 'HELP_FAQ_FORMATTING_BBOCDE_QUESTION' => 'HELP_FAQ_FORMATTING_BBOCDE_ANSWER', + 'HELP_FAQ_FORMATTING_HTML_QUESTION' => 'HELP_FAQ_FORMATTING_HTML_ANSWER', + 'HELP_FAQ_FORMATTING_SMILIES_QUESTION' => 'HELP_FAQ_FORMATTING_SMILIES_ANSWER', + 'HELP_FAQ_FORMATTING_IMAGES_QUESTION' => 'HELP_FAQ_FORMATTING_IMAGES_ANSWER', + 'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_QUESTION' => 'HELP_FAQ_FORMATTING_GLOBAL_ANNOUNCE_ANSWER', + 'HELP_FAQ_FORMATTING_ANNOUNCEMENT_QUESTION' => 'HELP_FAQ_FORMATTING_ANNOUNCEMENT_ANSWER', + 'HELP_FAQ_FORMATTING_STICKIES_QUESTION' => 'HELP_FAQ_FORMATTING_STICKIES_ANSWER', + 'HELP_FAQ_FORMATTING_LOCKED_QUESTION' => 'HELP_FAQ_FORMATTING_LOCKED_ANSWER', + 'HELP_FAQ_FORMATTING_ICONS_QUESTION' => 'HELP_FAQ_FORMATTING_ICONS_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_GROUPS', + true, + array( + 'HELP_FAQ_GROUPS_ADMINISTRATORS_QUESTION' => 'HELP_FAQ_GROUPS_ADMINISTRATORS_ANSWER', + 'HELP_FAQ_GROUPS_MODERATORS_QUESTION' => 'HELP_FAQ_GROUPS_MODERATORS_ANSWER', + 'HELP_FAQ_GROUPS_USERGROUPS_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_ANSWER', + 'HELP_FAQ_GROUPS_USERGROUPS_JOIN_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_JOIN_ANSWER', + 'HELP_FAQ_GROUPS_USERGROUPS_LEAD_QUESTION' => 'HELP_FAQ_GROUPS_USERGROUPS_LEAD_ANSWER', + 'HELP_FAQ_GROUPS_COLORS_QUESTION' => 'HELP_FAQ_GROUPS_COLORS_ANSWER', + 'HELP_FAQ_GROUPS_DEFAULT_QUESTION' => 'HELP_FAQ_GROUPS_DEFAULT_ANSWER', + 'HELP_FAQ_GROUPS_TEAM_QUESTION' => 'HELP_FAQ_GROUPS_TEAM_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_PMS', + false, + array( + 'HELP_FAQ_PMS_CANNOT_SEND_QUESTION' => 'HELP_FAQ_PMS_CANNOT_SEND_ANSWER', + 'HELP_FAQ_PMS_UNWANTED_QUESTION' => 'HELP_FAQ_PMS_UNWANTED_ANSWER', + 'HELP_FAQ_PMS_SPAM_QUESTION' => 'HELP_FAQ_PMS_SPAM_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_FRIENDS', + false, + array( + 'HELP_FAQ_FRIENDS_BASIC_QUESTION' => 'HELP_FAQ_FRIENDS_BASIC_ANSWER', + 'HELP_FAQ_FRIENDS_MANAGE_QUESTION' => 'HELP_FAQ_FRIENDS_MANAGE_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_SEARCH', + false, + array( + 'HELP_FAQ_SEARCH_FORUM_QUESTION' => 'HELP_FAQ_SEARCH_FORUM_ANSWER', + 'HELP_FAQ_SEARCH_NO_RESULT_QUESTION' => 'HELP_FAQ_SEARCH_NO_RESULT_ANSWER', + 'HELP_FAQ_SEARCH_BLANK_QUESTION' => 'HELP_FAQ_SEARCH_BLANK_ANSWER', + 'HELP_FAQ_SEARCH_MEMBERS_QUESTION' => 'HELP_FAQ_SEARCH_MEMBERS_ANSWER', + 'HELP_FAQ_SEARCH_OWN_QUESTION' => 'HELP_FAQ_SEARCH_OWN_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_BOOKMARKS', + false, + array( + 'HELP_FAQ_BOOKMARKS_DIFFERENCE_QUESTION' => 'HELP_FAQ_BOOKMARKS_DIFFERENCE_ANSWER', + 'HELP_FAQ_BOOKMARKS_TOPIC_QUESTION' => 'HELP_FAQ_BOOKMARKS_TOPIC_ANSWER', + 'HELP_FAQ_BOOKMARKS_FORUM_QUESTION' => 'HELP_FAQ_BOOKMARKS_FORUM_ANSWER', + 'HELP_FAQ_BOOKMARKS_REMOVE_QUESTION' => 'HELP_FAQ_BOOKMARKS_REMOVE_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_ATTACHMENTS', + false, + array( + 'HELP_FAQ_ATTACHMENTS_ALLOWED_QUESTION' => 'HELP_FAQ_ATTACHMENTS_ALLOWED_ANSWER', + 'HELP_FAQ_ATTACHMENTS_OWN_QUESTION' => 'HELP_FAQ_ATTACHMENTS_OWN_ANSWER', + ) + ); + $this->manager->add_block( + 'HELP_FAQ_BLOCK_ISSUES', + false, + array( + 'HELP_FAQ_ISSUES_WHOIS_PHPBB_QUESTION' => 'HELP_FAQ_ISSUES_WHOIS_PHPBB_ANSWER', + 'HELP_FAQ_ISSUES_FEATURE_QUESTION' => 'HELP_FAQ_ISSUES_FEATURE_ANSWER', + 'HELP_FAQ_ISSUES_LEGAL_QUESTION' => 'HELP_FAQ_ISSUES_LEGAL_ANSWER', + 'HELP_FAQ_ISSUES_ADMIN_QUESTION' => 'HELP_FAQ_ISSUES_ADMIN_ANSWER', + ) + ); + + return $this->language->lang('FAQ_EXPLAIN'); + } +} diff --git a/phpBB/phpbb/help/manager.php b/phpBB/phpbb/help/manager.php new file mode 100644 index 0000000000..d6991c0733 --- /dev/null +++ b/phpBB/phpbb/help/manager.php @@ -0,0 +1,136 @@ +<?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\help; + +/** + * Class help page manager + */ +class manager +{ + /** @var \phpbb\event\dispatcher */ + protected $dispatcher; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\template\template */ + protected $template; + + /** @var bool */ + protected $switched_column; + + /** + * Constructor + * + * @param \phpbb\event\dispatcher $dispatcher + * @param \phpbb\language\language $language + * @param \phpbb\template\template $template + */ + public function __construct(\phpbb\event\dispatcher $dispatcher, \phpbb\language\language $language, \phpbb\template\template $template) + { + $this->dispatcher = $dispatcher; + $this->language = $language; + $this->template = $template; + } + + /** + * Add a new faq block + * + * @param string $block_name Name or language key with the name of the block + * @param bool $switch_column Switch the column of the menu + */ + public function add_block($block_name, $switch_column = false, $questions = array()) + { + /** + * You can use this event to add a block before the current one. + * + * @event core.help_manager_add_block_before + * @var string block_name Language key of the block headline + * @var bool switch_column Should we switch the menu column before this headline + * @var array questions Array with questions + * @since 3.2.0-a1 + */ + $vars = array('block_name', 'switch_column', 'questions'); + extract($this->dispatcher->trigger_event('core.help_manager_add_block_before', compact($vars))); + + $this->template->assign_block_vars('faq_block', array( + 'BLOCK_TITLE' => $this->language->lang($block_name), + 'SWITCH_COLUMN' => !$this->switched_column && $switch_column, + )); + + foreach ($questions as $question => $answer) + { + $this->add_question($question, $answer); + } + + $this->switched_column = $this->switched_column || $switch_column; + + /** + * You can use this event to add a block after the current one. + * + * @event core.help_manager_add_block_after + * @var string block_name Language key of the block headline + * @var bool switch_column Should we switch the menu column before this headline + * @var array questions Array with questions + * @since 3.2.0-a1 + */ + $vars = array('block_name', 'switch_column', 'questions'); + extract($this->dispatcher->trigger_event('core.help_manager_add_block_after', compact($vars))); + } + + /** + * Add a new faq question + * + * @param string $question Question or language key with the question of the block + * @param string $answer Answer or language key with the answer of the block + */ + public function add_question($question, $answer) + { + /** + * You can use this event to add a question before the current one. + * + * @event core.help_manager_add_question_before + * @var string question Language key of the question + * @var string answer Language key of the answer + * @since 3.2.0-a1 + */ + $vars = array('question', 'answer'); + extract($this->dispatcher->trigger_event('core.help_manager_add_question_before', compact($vars))); + + $this->template->assign_block_vars('faq_block.faq_row', array( + 'FAQ_QUESTION' => $this->language->lang($question), + 'FAQ_ANSWER' => $this->language->lang($answer), + )); + + /** + * You can use this event to add a question after the current one. + * + * @event core.help_manager_add_question_after + * @var string question Language key of the question + * @var string answer Language key of the answer + * @since 3.2.0-a1 + */ + $vars = array('question', 'answer'); + extract($this->dispatcher->trigger_event('core.help_manager_add_question_after', compact($vars))); + } + + /** + * Returns whether the block titles switched side + * @return bool + */ + public function switched_column() + { + return $this->switched_column; + } +} diff --git a/phpBB/phpbb/language/language.php b/phpBB/phpbb/language/language.php index 3298908365..b2b9f5ce12 100644 --- a/phpBB/phpbb/language/language.php +++ b/phpBB/phpbb/language/language.php @@ -83,10 +83,7 @@ class language // Set up default information $this->user_language = false; $this->default_language = false; - $this->lang = array( - // For BC with user::help array - '__help' => array(), - ); + $this->lang = array(); $this->loaded_language_sets = array( 'core' => array(), 'ext' => array(), @@ -155,8 +152,6 @@ class language /** * Add Language Items * - * Note: $use_help is assigned where needed (only use them to force inclusion). - * * Examples: * <code> * $component = array('posting'); diff --git a/phpBB/phpbb/language/language_file_loader.php b/phpBB/phpbb/language/language_file_loader.php index 510a29279a..359202fd63 100644 --- a/phpBB/phpbb/language/language_file_loader.php +++ b/phpBB/phpbb/language/language_file_loader.php @@ -154,10 +154,12 @@ class language_file_loader * * @return string Relative path to language file * - * @throws \phpbb\language\exception\language_file_not_exists When the path to the file cannot be resolved + * @throws language_file_not_found When the path to the file cannot be resolved */ protected function get_language_file_path($path, $filename, $locales) { + $language_file_path = $filename; + // Language fallback logic foreach ($locales as $locale) { @@ -191,9 +193,6 @@ class language_file_loader */ protected function load_language_file($path, &$lang) { - // BC code for language files with help - $help = array(); - // Do not suppress error if in DEBUG mode if (defined('DEBUG')) { @@ -203,10 +202,5 @@ class language_file_loader { @include $path; } - - if (!empty($help)) - { - $lang['__help'] = array_merge($lang['__help'], $help); - } } } diff --git a/phpBB/phpbb/log/log.php b/phpBB/phpbb/log/log.php index 4bb2e7a75a..1b02d98b82 100644 --- a/phpBB/phpbb/log/log.php +++ b/phpBB/phpbb/log/log.php @@ -521,15 +521,77 @@ class log implements \phpbb\log\log_interface $sql_keywords = $this->generate_sql_keyword($keywords); } - if ($count_logs) - { - $sql = 'SELECT COUNT(l.log_id) AS total_entries - FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u - WHERE l.log_type = ' . (int) $log_type . ' + $get_logs_sql_ary = array( + 'SELECT' => 'l.*, u.username, u.username_clean, u.user_colour', + 'FROM' => array( + $this->log_table => 'l', + USERS_TABLE => 'u', + ), + 'WHERE' => 'l.log_type = ' . (int) $log_type . " AND l.user_id = u.user_id - AND l.log_time >= ' . (int) $log_time . " $sql_keywords - $sql_additional"; + $sql_additional", + + 'ORDER_BY' => $sort_by, + ); + + if ($log_time) + { + $get_logs_sql_ary['WHERE'] = 'l.log_time >= ' . (int) $log_time . ' + AND ' . $get_logs_sql_ary['WHERE']; + } + + /** + * Modify the query to obtain the logs data + * + * @event core.get_logs_main_query_before + * @var array get_logs_sql_ary The array in the format of the query builder with the query + * to get the log count and the log list + * @var string mode Mode of the entries we display + * @var bool count_logs Do we count all matching entries? + * @var int limit Limit the number of entries + * @var int offset Offset when fetching the entries + * @var mixed forum_id Limit entries to the forum_id, + * can also be an array of forum_ids + * @var int topic_id Limit entries to the topic_id + * @var int user_id Limit entries to the user_id + * @var int log_time Limit maximum age of log entries + * @var string sort_by SQL order option + * @var string keywords Will only return entries that have the + * keywords in log_operation or log_data + * @var string profile_url URL to the users profile + * @var int log_type Limit logs to a certain type. If log_type + * is false, no entries will be returned. + * @var string sql_additional Additional conditions for the entries, + * e.g.: 'AND l.forum_id = 1' + * @since 3.1.5-RC1 + */ + $vars = array( + 'get_logs_sql_ary', + 'mode', + 'count_logs', + 'limit', + 'offset', + 'forum_id', + 'topic_id', + 'user_id', + 'log_time', + 'sort_by', + 'keywords', + 'profile_url', + 'log_type', + 'sql_additional', + ); + extract($this->dispatcher->trigger_event('core.get_logs_main_query_before', compact($vars))); + + if ($count_logs) + { + $count_logs_sql_ary = $get_logs_sql_ary; + + $count_logs_sql_ary['SELECT'] = 'COUNT(l.log_id) AS total_entries'; + unset($count_logs_sql_ary['ORDER_BY']); + + $sql = $this->db->sql_build_query('SELECT', $count_logs_sql_ary); $result = $this->db->sql_query($sql); $this->entry_count = (int) $this->db->sql_fetchfield('total_entries'); $this->db->sql_freeresult($result); @@ -548,14 +610,7 @@ class log implements \phpbb\log\log_interface } } - $sql = 'SELECT l.*, u.username, u.username_clean, u.user_colour - FROM ' . $this->log_table . ' l, ' . USERS_TABLE . ' u - WHERE l.log_type = ' . (int) $log_type . ' - AND u.user_id = l.user_id - ' . (($log_time) ? 'AND l.log_time >= ' . (int) $log_time : '') . " - $sql_keywords - $sql_additional - ORDER BY $sort_by"; + $sql = $this->db->sql_build_query('SELECT', $get_logs_sql_ary); $result = $this->db->sql_query_limit($sql, $limit, $this->last_page_offset); $i = 0; diff --git a/phpBB/phpbb/module/exception/module_exception.php b/phpBB/phpbb/module/exception/module_exception.php new file mode 100644 index 0000000000..8ad75112bc --- /dev/null +++ b/phpBB/phpbb/module/exception/module_exception.php @@ -0,0 +1,19 @@ +<?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\module\exception; + +class module_exception extends \phpbb\exception\runtime_exception +{ + +} diff --git a/phpBB/phpbb/module/exception/module_not_found_exception.php b/phpBB/phpbb/module/exception/module_not_found_exception.php new file mode 100644 index 0000000000..2d485e7b35 --- /dev/null +++ b/phpBB/phpbb/module/exception/module_not_found_exception.php @@ -0,0 +1,19 @@ +<?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\module\exception; + +class module_not_found_exception extends module_exception +{ + +} diff --git a/phpBB/phpbb/module/module_manager.php b/phpBB/phpbb/module/module_manager.php new file mode 100644 index 0000000000..a812d06736 --- /dev/null +++ b/phpBB/phpbb/module/module_manager.php @@ -0,0 +1,564 @@ +<?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\module; + +use phpbb\module\exception\module_exception; +use phpbb\module\exception\module_not_found_exception; + +class module_manager +{ + /** + * @var \phpbb\cache\driver\driver_interface + */ + protected $cache; + + /** + * @var \phpbb\db\driver\driver_interface + */ + protected $db; + + /** + * @var \phpbb\extension\manager + */ + protected $extension_manager; + + /** + * @var string + */ + protected $modules_table; + + /** + * @var string + */ + protected $phpbb_root_path; + + /** + * @var string + */ + protected $php_ext; + + /** + * Constructor + * + * @param \phpbb\cache\driver\driver_interface $cache Cache driver + * @param \phpbb\db\driver\driver_interface $db Database driver + * @param \phpbb\extension\manager $ext_manager Extension manager + * @param string $modules_table Module database table's name + * @param string $phpbb_root_path Path to phpBB's root + * @param string $php_ext Extension of PHP files + */ + public function __construct(\phpbb\cache\driver\driver_interface $cache, \phpbb\db\driver\driver_interface $db, \phpbb\extension\manager $ext_manager, $modules_table, $phpbb_root_path, $php_ext) + { + $this->cache = $cache; + $this->db = $db; + $this->extension_manager = $ext_manager; + $this->modules_table = $modules_table; + $this->phpbb_root_path = $phpbb_root_path; + $this->php_ext = $php_ext; + } + + /** + * Get row for specified module + * + * @param int $module_id ID of the module + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * + * @return array Array of data fetched from the database + * + * @throws \phpbb\module\exception\module_not_found_exception When there is no module with $module_id + */ + public function get_module_row($module_id, $module_class) + { + $module_id = (int) $module_id; + + $sql = 'SELECT * + FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND module_id = $module_id"; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + if (!$row) + { + throw new module_not_found_exception('NO_MODULE'); + } + + return $row; + } + + /** + * Get available module information from module files + * + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * @param string $module ID of module + * @param bool $use_all_available Use all available instead of just all + * enabled extensions + * + * @return array Array with module information gathered from module info files. + */ + public function get_module_infos($module_class, $module = '', $use_all_available = false) + { + $directory = $this->phpbb_root_path . 'includes/' . $module_class . '/info/'; + $fileinfo = array(); + + $finder = $this->extension_manager->get_finder($use_all_available); + + $modules = $finder + ->extension_suffix('_module') + ->extension_directory("/$module_class") + ->core_path("includes/$module_class/info/") + ->core_prefix($module_class . '_') + ->get_classes(true); + + foreach ($modules as $cur_module) + { + // Skip entries we do not need if we know the module we are + // looking for + if ($module && strpos(str_replace('\\', '_', $cur_module), $module) === false && $module !== $cur_module) + { + continue; + } + + $info_class = preg_replace('/_module$/', '_info', $cur_module); + + // If the class does not exist it might be following the old + // format. phpbb_acp_info_acp_foo needs to be turned into + // acp_foo_info and the respective file has to be included + // manually because it does not support auto loading + $old_info_class_file = str_replace("phpbb_{$module_class}_info_", '', $cur_module); + $old_info_class = $old_info_class_file . '_info'; + + if (class_exists($old_info_class)) + { + $info_class = $old_info_class; + } + else if (!class_exists($info_class)) + { + $info_class = $old_info_class; + + // need to check class exists again because previous checks triggered autoloading + if (!class_exists($info_class) && file_exists($directory . $old_info_class_file . '.' . $this->php_ext)) + { + include($directory . $old_info_class_file . '.' . $this->php_ext); + } + } + + if (class_exists($info_class)) + { + $info = new $info_class(); + $module_info = $info->module(); + + $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $cur_module; + + $fileinfo[$main_class] = $module_info; + } + } + + ksort($fileinfo); + + return $fileinfo; + } + + /** + * Get module branch + * + * @param int $module_id ID of the module + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * @param string $type Type of branch (Expected values: all, parents or children) + * @param bool $include_module Whether or not to include the specified module with $module_id + * + * @return array Returns an array containing the modules in the specified branch type. + */ + public function get_module_branch($module_id, $module_class, $type = 'all', $include_module = true) + { + $module_id = (int) $module_id; + + switch ($type) + { + case 'parents': + $condition = 'm1.left_id BETWEEN m2.left_id AND m2.right_id'; + break; + + case 'children': + $condition = 'm2.left_id BETWEEN m1.left_id AND m1.right_id'; + break; + + default: + $condition = 'm2.left_id BETWEEN m1.left_id AND m1.right_id OR m1.left_id BETWEEN m2.left_id AND m2.right_id'; + break; + } + + $rows = array(); + + $sql = 'SELECT m2.* + FROM ' . $this->modules_table . ' m1 + LEFT JOIN ' . $this->modules_table . " m2 ON ($condition) + WHERE m1.module_class = '" . $this->db->sql_escape($module_class) . "' + AND m2.module_class = '" . $this->db->sql_escape($module_class) . "' + AND m1.module_id = $module_id + ORDER BY m2.left_id DESC"; + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + if (!$include_module && $row['module_id'] == $module_id) + { + continue; + } + + $rows[] = $row; + } + $this->db->sql_freeresult($result); + + return $rows; + } + + /** + * Remove modules cache file + * + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + */ + public function remove_cache_file($module_class) + { + // Sanitise for future path use, it's escaped as appropriate for queries + $cache_class = str_replace(array('.', '/', '\\'), '', basename($module_class)); + $this->cache->destroy('_modules_' . $cache_class); + $this->cache->destroy('sql', $this->modules_table); + } + + /** + * Update/Add module + * + * @param array &$module_data The module data + * + * @throws \phpbb\module\exception\module_not_found_exception When parent module or the category is not exist + */ + public function update_module_data(&$module_data) + { + if (!isset($module_data['module_id'])) + { + // no module_id means we're creating a new category/module + if ($module_data['parent_id']) + { + $sql = 'SELECT left_id, right_id + FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "' + AND module_id = " . (int) $module_data['parent_id']; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + if (!$row) + { + throw new module_not_found_exception('PARENT_NOT_EXIST'); + } + + // Workaround + $row['left_id'] = (int) $row['left_id']; + $row['right_id'] = (int) $row['right_id']; + + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id + 2, right_id = right_id + 2 + WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "' + AND left_id > {$row['right_id']}"; + $this->db->sql_query($sql); + + $sql = 'UPDATE ' . $this->modules_table . " + SET right_id = right_id + 2 + WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "' + AND {$row['left_id']} BETWEEN left_id AND right_id"; + $this->db->sql_query($sql); + + $module_data['left_id'] = (int) $row['right_id']; + $module_data['right_id'] = (int) $row['right_id'] + 1; + } + else + { + $sql = 'SELECT MAX(right_id) AS right_id + FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "'"; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $module_data['left_id'] = (int) $row['right_id'] + 1; + $module_data['right_id'] = (int) $row['right_id'] + 2; + } + + $sql = 'INSERT INTO ' . $this->modules_table . ' ' . $this->db->sql_build_array('INSERT', $module_data); + $this->db->sql_query($sql); + + $module_data['module_id'] = $this->db->sql_nextid(); + } + else + { + $row = $this->get_module_row($module_data['module_id'], $module_data['module_class']); + + if ($module_data['module_basename'] && !$row['module_basename']) + { + // we're turning a category into a module + $branch = $this->get_module_branch($module_data['module_id'], $module_data['module_class'], 'children', false); + + if (sizeof($branch)) + { + throw new module_not_found_exception('NO_CATEGORY_TO_MODULE'); + } + } + + if ($row['parent_id'] != $module_data['parent_id']) + { + $this->move_module($module_data['module_id'], $module_data['parent_id'], $module_data['module_class']); + } + + $update_ary = $module_data; + unset($update_ary['module_id']); + + $sql = 'UPDATE ' . $this->modules_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $update_ary) . " + WHERE module_class = '" . $this->db->sql_escape($module_data['module_class']) . "' + AND module_id = " . (int) $module_data['module_id']; + $this->db->sql_query($sql); + } + } + + /** + * Move module around the tree + * + * @param int $from_module_id ID of the current parent module + * @param int $to_parent_id ID of the target parent module + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * + * @throws \phpbb\module\exception\module_not_found_exception If the module specified to move modules from does not + * have any children. + */ + public function move_module($from_module_id, $to_parent_id, $module_class) + { + $moved_modules = $this->get_module_branch($from_module_id, $module_class, 'children'); + + if (empty($moved_modules)) + { + throw new module_not_found_exception(); + } + + $from_data = $moved_modules[0]; + $diff = sizeof($moved_modules) * 2; + + $moved_ids = array(); + for ($i = 0; $i < sizeof($moved_modules); ++$i) + { + $moved_ids[] = $moved_modules[$i]['module_id']; + } + + // Resync parents + $sql = 'UPDATE ' . $this->modules_table . " + SET right_id = right_id - $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id < " . (int) $from_data['right_id'] . ' + AND right_id > ' . (int) $from_data['right_id']; + $this->db->sql_query($sql); + + // Resync righthand side of tree + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id - $diff, right_id = right_id - $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id > " . (int) $from_data['right_id']; + $this->db->sql_query($sql); + + if ($to_parent_id > 0) + { + $to_data = $this->get_module_row($to_parent_id, $module_class); + + // Resync new parents + $sql = 'UPDATE ' . $this->modules_table . " + SET right_id = right_id + $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND " . (int) $to_data['right_id'] . ' BETWEEN left_id AND right_id + AND ' . $this->db->sql_in_set('module_id', $moved_ids, true); + $this->db->sql_query($sql); + + // Resync the righthand side of the tree + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id + $diff, right_id = right_id + $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id > " . (int) $to_data['right_id'] . ' + AND ' . $this->db->sql_in_set('module_id', $moved_ids, true); + $this->db->sql_query($sql); + + // Resync moved branch + $to_data['right_id'] += $diff; + if ($to_data['right_id'] > $from_data['right_id']) + { + $diff = '+ ' . ($to_data['right_id'] - $from_data['right_id'] - 1); + } + else + { + $diff = '- ' . abs($to_data['right_id'] - $from_data['right_id'] - 1); + } + } + else + { + $sql = 'SELECT MAX(right_id) AS right_id + FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND " . $this->db->sql_in_set('module_id', $moved_ids, true); + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $diff = '+ ' . (int) ($row['right_id'] - $from_data['left_id'] + 1); + } + + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id $diff, right_id = right_id $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND " . $this->db->sql_in_set('module_id', $moved_ids); + $this->db->sql_query($sql); + } + + /** + * Remove module from tree + * + * @param int $module_id ID of the module to delete + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * + * @throws \phpbb\module\exception\module_exception When the specified module cannot be removed + */ + public function delete_module($module_id, $module_class) + { + $module_id = (int) $module_id; + + $row = $this->get_module_row($module_id, $module_class); + + $branch = $this->get_module_branch($module_id, $module_class, 'children', false); + + if (sizeof($branch)) + { + throw new module_exception('CANNOT_REMOVE_MODULE'); + } + + // If not move + $diff = 2; + $sql = 'DELETE FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND module_id = $module_id"; + $this->db->sql_query($sql); + + $row['right_id'] = (int) $row['right_id']; + $row['left_id'] = (int) $row['left_id']; + + // Resync tree + $sql = 'UPDATE ' . $this->modules_table . " + SET right_id = right_id - $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id < {$row['right_id']} AND right_id > {$row['right_id']}"; + $this->db->sql_query($sql); + + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id - $diff, right_id = right_id - $diff + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id > {$row['right_id']}"; + $this->db->sql_query($sql); + } + + /** + * Move module position by $steps up/down + * + * @param array $module_row Array of module data + * @param string $module_class Class of the module (acp, ucp, mcp etc...) + * @param string $action Direction of moving (valid values: move_up or move_down) + * @param int $steps Number of steps to move module + * + * @return string Returns the language name of the module + * + * @throws \phpbb\module\exception\module_not_found_exception When the specified module does not exists + */ + public function move_module_by($module_row, $module_class, $action = 'move_up', $steps = 1) + { + /** + * Fetch all the siblings between the module's current spot + * and where we want to move it to. If there are less than $steps + * siblings between the current spot and the target then the + * module will move as far as possible + */ + $sql = 'SELECT module_id, left_id, right_id, module_langname + FROM ' . $this->modules_table . " + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND parent_id = " . (int) $module_row['parent_id'] . ' + AND ' . (($action == 'move_up') ? 'right_id < ' . (int) $module_row['right_id'] . ' ORDER BY right_id DESC' : 'left_id > ' . (int) $module_row['left_id'] . ' ORDER BY left_id ASC'); + $result = $this->db->sql_query_limit($sql, $steps); + + $target = array(); + while ($row = $this->db->sql_fetchrow($result)) + { + $target = $row; + } + $this->db->sql_freeresult($result); + + if (!sizeof($target)) + { + // The module is already on top or bottom + throw new module_not_found_exception(); + } + + /** + * $left_id and $right_id define the scope of the nodes that are affected by the move. + * $diff_up and $diff_down are the values to substract or add to each node's left_id + * and right_id in order to move them up or down. + * $move_up_left and $move_up_right define the scope of the nodes that are moving + * up. Other nodes in the scope of ($left_id, $right_id) are considered to move down. + */ + if ($action == 'move_up') + { + $left_id = (int) $target['left_id']; + $right_id = (int) $module_row['right_id']; + + $diff_up = (int) ($module_row['left_id'] - $target['left_id']); + $diff_down = (int) ($module_row['right_id'] + 1 - $module_row['left_id']); + + $move_up_left = (int) $module_row['left_id']; + $move_up_right = (int) $module_row['right_id']; + } + else + { + $left_id = (int) $module_row['left_id']; + $right_id = (int) $target['right_id']; + + $diff_up = (int) ($module_row['right_id'] + 1 - $module_row['left_id']); + $diff_down = (int) ($target['right_id'] - $module_row['right_id']); + + $move_up_left = (int) ($module_row['right_id'] + 1); + $move_up_right = (int) $target['right_id']; + } + + // Now do the dirty job + $sql = 'UPDATE ' . $this->modules_table . " + SET left_id = left_id + CASE + WHEN left_id BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up} + ELSE {$diff_down} + END, + right_id = right_id + CASE + WHEN right_id BETWEEN {$move_up_left} AND {$move_up_right} THEN -{$diff_up} + ELSE {$diff_down} + END + WHERE module_class = '" . $this->db->sql_escape($module_class) . "' + AND left_id BETWEEN {$left_id} AND {$right_id} + AND right_id BETWEEN {$left_id} AND {$right_id}"; + $this->db->sql_query($sql); + + $this->remove_cache_file($module_class); + + return $target['module_langname']; + } +} diff --git a/phpBB/phpbb/notification/method/base.php b/phpBB/phpbb/notification/method/base.php index a0bbed6fcd..6ee1d2984a 100644 --- a/phpBB/phpbb/notification/method/base.php +++ b/phpBB/phpbb/notification/method/base.php @@ -60,7 +60,7 @@ abstract class base implements \phpbb\notification\method\method_interface /** * Notification Method Base Constructor - * + * * @param \phpbb\user_loader $user_loader * @param \phpbb\db\driver\driver_interface $db * @param \phpbb\cache\driver\driver_interface $cache @@ -85,7 +85,7 @@ abstract class base implements \phpbb\notification\method\method_interface /** * Set notification manager (required) - * + * * @param \phpbb\notification\manager $notification_manager */ public function set_notification_manager(\phpbb\notification\manager $notification_manager) diff --git a/phpBB/phpbb/notification/type/admin_activate_user.php b/phpBB/phpbb/notification/type/admin_activate_user.php index 73ed612480..0e2924e29c 100644 --- a/phpBB/phpbb/notification/type/admin_activate_user.php +++ b/phpBB/phpbb/notification/type/admin_activate_user.php @@ -104,7 +104,7 @@ class admin_activate_user extends \phpbb\notification\type\base */ public function get_avatar() { - return $this->user_loader->get_avatar($this->item_id); + return $this->user_loader->get_avatar($this->item_id, false, true); } /** diff --git a/phpBB/phpbb/notification/type/group_request.php b/phpBB/phpbb/notification/type/group_request.php index 19665624df..2d7925c1b4 100644 --- a/phpBB/phpbb/notification/type/group_request.php +++ b/phpBB/phpbb/notification/type/group_request.php @@ -96,7 +96,7 @@ class group_request extends \phpbb\notification\type\base */ public function get_avatar() { - return $this->user_loader->get_avatar($this->item_id); + return $this->user_loader->get_avatar($this->item_id, false, true); } /** diff --git a/phpBB/phpbb/notification/type/pm.php b/phpBB/phpbb/notification/type/pm.php index 29b4b79216..5d4f7fe0c6 100644 --- a/phpBB/phpbb/notification/type/pm.php +++ b/phpBB/phpbb/notification/type/pm.php @@ -100,7 +100,7 @@ class pm extends \phpbb\notification\type\base */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('from_user_id')); + return $this->user_loader->get_avatar($this->get_data('from_user_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/post.php b/phpBB/phpbb/notification/type/post.php index d6aa8a8af9..f6b3136a21 100644 --- a/phpBB/phpbb/notification/type/post.php +++ b/phpBB/phpbb/notification/type/post.php @@ -165,7 +165,7 @@ class post extends \phpbb\notification\type\base */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('poster_id')); + return $this->user_loader->get_avatar($this->get_data('poster_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/quote.php b/phpBB/phpbb/notification/type/quote.php index 141f90c7ae..51edfec6f7 100644 --- a/phpBB/phpbb/notification/type/quote.php +++ b/phpBB/phpbb/notification/type/quote.php @@ -21,6 +21,11 @@ namespace phpbb\notification\type; class quote extends \phpbb\notification\type\post { /** + * @var \phpbb\textformatter\utils_interface + */ + protected $utils; + + /** * Get notification type name * * @return string @@ -31,13 +36,6 @@ class quote extends \phpbb\notification\type\post } /** - * regular expression to match to find usernames - * - * @var string - */ - protected static $regular_expression_match = '#\[quote="(.+?)"#'; - - /** * Language key used to output the text * * @var string @@ -77,17 +75,16 @@ class quote extends \phpbb\notification\type\post 'ignore_users' => array(), ), $options); - $usernames = false; - preg_match_all(self::$regular_expression_match, $post['post_text'], $usernames); + $usernames = $this->utils->get_outermost_quote_authors($post['post_text']); - if (empty($usernames[1])) + if (empty($usernames)) { return array(); } - $usernames[1] = array_unique($usernames[1]); + $usernames = array_unique($usernames); - $usernames = array_map('utf8_clean_string', $usernames[1]); + $usernames = array_map('utf8_clean_string', $usernames); $users = array(); @@ -187,4 +184,14 @@ class quote extends \phpbb\notification\type\post 'AUTHOR_NAME' => htmlspecialchars_decode($user_data['username']), )); } + + /** + * Set the utils service used to retrieve quote authors + * + * @param \phpbb\textformatter\utils_interface $utils + */ + public function set_utils(\phpbb\textformatter\utils_interface $utils) + { + $this->utils = $utils; + } } diff --git a/phpBB/phpbb/notification/type/report_pm.php b/phpBB/phpbb/notification/type/report_pm.php index 1904680d5a..e7d385f59f 100644 --- a/phpBB/phpbb/notification/type/report_pm.php +++ b/phpBB/phpbb/notification/type/report_pm.php @@ -223,7 +223,7 @@ class report_pm extends \phpbb\notification\type\pm */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('reporter_id')); + return $this->user_loader->get_avatar($this->get_data('reporter_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/report_pm_closed.php b/phpBB/phpbb/notification/type/report_pm_closed.php index 9f301ee2cc..1c99db60c3 100644 --- a/phpBB/phpbb/notification/type/report_pm_closed.php +++ b/phpBB/phpbb/notification/type/report_pm_closed.php @@ -130,7 +130,7 @@ class report_pm_closed extends \phpbb\notification\type\pm */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('closer_id')); + return $this->user_loader->get_avatar($this->get_data('closer_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/report_post.php b/phpBB/phpbb/notification/type/report_post.php index b64862078a..8289a04cf1 100644 --- a/phpBB/phpbb/notification/type/report_post.php +++ b/phpBB/phpbb/notification/type/report_post.php @@ -196,7 +196,7 @@ class report_post extends \phpbb\notification\type\post_in_queue */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('reporter_id')); + return $this->user_loader->get_avatar($this->get_data('reporter_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/report_post_closed.php b/phpBB/phpbb/notification/type/report_post_closed.php index a0bb187a0d..3f4378628b 100644 --- a/phpBB/phpbb/notification/type/report_post_closed.php +++ b/phpBB/phpbb/notification/type/report_post_closed.php @@ -137,7 +137,7 @@ class report_post_closed extends \phpbb\notification\type\post */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('closer_id')); + return $this->user_loader->get_avatar($this->get_data('closer_id'), false, true); } /** diff --git a/phpBB/phpbb/notification/type/topic.php b/phpBB/phpbb/notification/type/topic.php index a1a17535b5..f0681b9eb8 100644 --- a/phpBB/phpbb/notification/type/topic.php +++ b/phpBB/phpbb/notification/type/topic.php @@ -119,7 +119,7 @@ class topic extends \phpbb\notification\type\base */ public function get_avatar() { - return $this->user_loader->get_avatar($this->get_data('poster_id')); + return $this->user_loader->get_avatar($this->get_data('poster_id'), false, true); } /** diff --git a/phpBB/phpbb/passwords/driver/helper.php b/phpBB/phpbb/passwords/driver/helper.php index caa65080ac..f80c3e3df6 100644 --- a/phpBB/phpbb/passwords/driver/helper.php +++ b/phpBB/phpbb/passwords/driver/helper.php @@ -153,11 +153,23 @@ class helper */ public function string_compare($string_a, $string_b) { - $difference = strlen($string_a) != strlen($string_b); + // Return if input variables are not strings or if length does not match + if (!is_string($string_a) || !is_string($string_b) || strlen($string_a) != strlen($string_b)) + { + return false; + } + + // Use hash_equals() if it's available + if (function_exists('hash_equals')) + { + return hash_equals($string_a, $string_b); + } + + $difference = 0; for ($i = 0; $i < strlen($string_a) && $i < strlen($string_b); $i++) { - $difference |= $string_a[$i] != $string_b[$i]; + $difference |= ord($string_a[$i]) ^ ord($string_b[$i]); } return $difference === 0; diff --git a/phpBB/phpbb/routing/router.php b/phpBB/phpbb/routing/router.php index dd5bffe22b..5af005769f 100644 --- a/phpBB/phpbb/routing/router.php +++ b/phpBB/phpbb/routing/router.php @@ -14,6 +14,10 @@ namespace phpbb\routing; use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper; use Symfony\Component\Routing\Matcher\UrlMatcher; @@ -91,18 +95,24 @@ class router implements RouterInterface protected $filesystem; /** + * @var ContainerInterface + */ + protected $container; + + /** * Construct method * + * @param ContainerInterface $container DI container * @param \phpbb\filesystem\filesystem_interface $filesystem Filesystem helper - * @param string $phpbb_root_path phpBB root path - * @param string $php_ext PHP file extension - * @param string $environment Name of the current environment - * @param manager|null $extension_manager Extension manager - * @param array $routing_files Array of strings containing paths to YAML files - * holding route information + * @param string $phpbb_root_path phpBB root path + * @param string $php_ext PHP file extension + * @param string $environment Name of the current environment + * @param manager $extension_manager Extension manager + * @param array $routing_files Array of strings containing paths to YAML files holding route information */ - public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path, $php_ext, $environment, manager $extension_manager = null, $routing_files = array()) + public function __construct(ContainerInterface $container, \phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path, $php_ext, $environment, manager $extension_manager = null, $routing_files = array()) { + $this->container = $container; $this->filesystem = $filesystem; $this->extension_manager = $extension_manager; $this->routing_files = $routing_files; @@ -161,10 +171,118 @@ class router implements RouterInterface } } + $this->resolveParameters($this->route_collection); + return $this; } /** + * Replaces placeholders with service container parameter values in: + * - the route defaults, + * - the route requirements, + * - the route path, + * - the route host, + * - the route schemes, + * - the route methods. + * + * @param RouteCollection $collection + */ + private function resolveParameters(RouteCollection $collection) + { + foreach ($collection as $route) + { + foreach ($route->getDefaults() as $name => $value) + { + $route->setDefault($name, $this->resolve($value)); + } + + $requirements = $route->getRequirements(); + unset($requirements['_scheme']); + unset($requirements['_method']); + + foreach ($requirements as $name => $value) + { + $route->setRequirement($name, $this->resolve($value)); + } + + $route->setPath($this->resolve($route->getPath())); + $route->setHost($this->resolve($route->getHost())); + + $schemes = array(); + foreach ($route->getSchemes() as $scheme) + { + $schemes = array_merge($schemes, explode('|', $this->resolve($scheme))); + } + + $route->setSchemes($schemes); + $methods = array(); + foreach ($route->getMethods() as $method) + { + $methods = array_merge($methods, explode('|', $this->resolve($method))); + } + + $route->setMethods($methods); + $route->setCondition($this->resolve($route->getCondition())); + } + } + + /** + * Recursively replaces placeholders with the service container parameters. + * + * @param mixed $value The source which might contain "%placeholders%" + * + * @return mixed The source with the placeholders replaced by the container + * parameters. Arrays are resolved recursively. + * + * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter + * @throws RuntimeException When a container value is not a string or a numeric value + */ + private function resolve($value) + { + if (is_array($value)) + { + foreach ($value as $key => $val) + { + $value[$key] = $this->resolve($val); + } + + return $value; + } + + if (!is_string($value)) + { + return $value; + } + + $container = $this->container; + $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value) + { + // skip %% + if (!isset($match[1])) + { + return '%%'; + } + + $resolved = $container->getParameter($match[1]); + if (is_string($resolved) || is_numeric($resolved)) + { + return (string) $resolved; + } + + throw new RuntimeException(sprintf( + 'The container parameter "%s", used in the route configuration value "%s", '. + 'must be a string or numeric, but it is of type %s.', + $match[1], + $value, + gettype($resolved) + ) + ); + }, $value); + + return str_replace('%%', '%', $escapedValue); + } + + /** * Get the list of routes * * @return RouteCollection Get the route collection @@ -252,22 +370,29 @@ class router implements RouterInterface */ protected function create_dumped_url_matcher() { - $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_matcher.{$this->php_ext}", defined('DEBUG')); - if (!$cache->isFresh()) + try { - $dumper = new PhpMatcherDumper($this->get_routes()); + $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_matcher.{$this->php_ext}", defined('DEBUG')); + if (!$cache->isFresh()) + { + $dumper = new PhpMatcherDumper($this->get_routes()); - $options = array( - 'class' => 'phpbb_url_matcher', - 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', - ); + $options = array( + 'class' => 'phpbb_url_matcher', + 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + ); - $cache->write($dumper->dump($options), $this->get_routes()->getResources()); - } + $cache->write($dumper->dump($options), $this->get_routes()->getResources()); + } - require_once($cache->getPath()); + require_once($cache->getPath()); - $this->matcher = new \phpbb_url_matcher($this->context); + $this->matcher = new \phpbb_url_matcher($this->context); + } + catch (IOException $e) + { + $this->create_new_url_matcher(); + } } /** @@ -300,22 +425,29 @@ class router implements RouterInterface */ protected function create_dumped_url_generator() { - $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_generator.{$this->php_ext}", defined('DEBUG')); - if (!$cache->isFresh()) + try { - $dumper = new PhpGeneratorDumper($this->get_routes()); + $cache = new ConfigCache("{$this->phpbb_root_path}cache/{$this->environment}/url_generator.{$this->php_ext}", defined('DEBUG')); + if (!$cache->isFresh()) + { + $dumper = new PhpGeneratorDumper($this->get_routes()); - $options = array( - 'class' => 'phpbb_url_generator', - 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', - ); + $options = array( + 'class' => 'phpbb_url_generator', + 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + ); - $cache->write($dumper->dump($options), $this->get_routes()->getResources()); - } + $cache->write($dumper->dump($options), $this->get_routes()->getResources()); + } - require_once($cache->getPath()); + require_once($cache->getPath()); - $this->generator = new \phpbb_url_generator($this->context); + $this->generator = new \phpbb_url_generator($this->context); + } + catch (IOException $e) + { + $this->create_new_url_generator(); + } } /** diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php index da9de56009..9f1aaec7c2 100644 --- a/phpBB/phpbb/search/fulltext_mysql.php +++ b/phpBB/phpbb/search/fulltext_mysql.php @@ -43,6 +43,12 @@ class fulltext_mysql extends \phpbb\search\base protected $db; /** + * phpBB event dispatcher object + * @var \phpbb\event\dispatcher_interface + */ + protected $phpbb_dispatcher; + + /** * User object * @var \phpbb\user */ @@ -79,11 +85,13 @@ class fulltext_mysql extends \phpbb\search\base * @param \phpbb\config\config $config Config object * @param \phpbb\db\driver\driver_interface Database object * @param \phpbb\user $user User object + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object */ - public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user) + public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher) { $this->config = $config; $this->db = $db; + $this->phpbb_dispatcher = $phpbb_dispatcher; $this->user = $user; $this->word_length = array('min' => $this->config['fulltext_mysql_min_word_len'], 'max' => $this->config['fulltext_mysql_max_word_len']); @@ -447,6 +455,55 @@ class fulltext_mysql extends \phpbb\search\base break; } + $search_query = $this->search_query; + + /** + * Allow changing the query used to search for posts using fulltext_mysql + * + * @event core.search_mysql_keywords_main_query_before + * @var string search_query The parsed keywords used for this search + * @var int result_count The previous result count for the format of the query. + * Set to 0 to force a re-count + * @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED + * @var array author_ary Array of user_id containing the users to filter the results to + * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant) + * @var array ex_fid_ary Which forums not to search on + * @var int topic_id Limit the search to this topic_id only + * @var string sql_sort_table Extra tables to include in the SQL query. + * Used in conjunction with sql_sort_join + * @var string sql_sort_join SQL conditions to join all the tables used together. + * Used in conjunction with sql_sort_table + * @var int sort_days Time, in days, of the oldest possible post to list + * @var string sql_match Which columns to do the search on. + * @var string sql_match_where Extra conditions to use to properly filter the matching process + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'search_query', + 'result_count', + 'join_topic', + 'author_ary', + 'author_name', + 'ex_fid_ary', + 'topic_id', + 'sql_sort_table', + 'sql_sort_join', + 'sort_days', + 'sql_match', + 'sql_match_where', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_keywords_main_query_before', compact($vars))); + $sql_select = (!$result_count) ? 'SQL_CALC_FOUND_ROWS ' : ''; $sql_select = ($type == 'posts') ? $sql_select . 'p.post_id' : 'DISTINCT ' . $sql_select . 't.topic_id'; $sql_from = ($join_topic) ? TOPICS_TABLE . ' t, ' : ''; @@ -620,6 +677,55 @@ class fulltext_mysql extends \phpbb\search\base $m_approve_fid_sql = ' AND ' . $post_visibility; + /** + * Allow changing the query used to search for posts by author in fulltext_mysql + * + * @event core.search_mysql_author_query_before + * @var int result_count The previous result count for the format of the query. + * Set to 0 to force a re-count + * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen + * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table + * @var array author_ary Array of user_id containing the users to filter the results to + * @var string author_name An extra username to search on + * @var string sql_author SQL WHERE condition for the post author ids + * @var int topic_id Limit the search to this topic_id only + * @var string sql_topic_id SQL of topic_id + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var string sort_days Time, in days, that the oldest post showing can have + * @var string sql_time The SQL to search on the time specifyed by sort_days + * @var bool firstpost_only Wether or not to search only on the first post of the topics + * @var array ex_fid_ary Forum ids that must not be searched on + * @var array sql_fora SQL query for ex_fid_ary + * @var string m_approve_fid_sql WHERE clause condition on post_visibility restrictions + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'result_count', + 'sql_sort_table', + 'sql_sort_join', + 'author_ary', + 'author_name', + 'sql_author', + 'topic_id', + 'sql_topic_id', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'sort_days', + 'sql_time', + 'firstpost_only', + 'ex_fid_ary', + 'sql_fora', + 'm_approve_fid_sql', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_mysql_author_query_before', compact($vars))); + // If the cache was completely empty count the results $calc_results = ($result_count) ? '' : 'SQL_CALC_FOUND_ROWS '; diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php index 42ad97da30..43f399eb13 100644 --- a/phpBB/phpbb/search/fulltext_native.php +++ b/phpBB/phpbb/search/fulltext_native.php @@ -94,6 +94,12 @@ class fulltext_native extends \phpbb\search\base protected $db; /** + * phpBB event dispatcher object + * @var \phpbb\event\dispatcher_interface + */ + protected $phpbb_dispatcher; + + /** * User object * @var \phpbb\user */ @@ -103,13 +109,15 @@ class fulltext_native extends \phpbb\search\base * Initialises the fulltext_native search backend with min/max word length * * @param boolean|string &$error is passed by reference and should either be set to false on success or an error message on failure + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object */ - public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user) + public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher) { $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $phpEx; $this->config = $config; $this->db = $db; + $this->phpbb_dispatcher = $phpbb_dispatcher; $this->user = $user; $this->word_length = array('min' => $this->config['fulltext_native_min_chars'], 'max' => $this->config['fulltext_native_max_chars']); @@ -715,6 +723,68 @@ class fulltext_native extends \phpbb\search\base $sql_where[] = $post_visibility; + $search_query = $this->search_query; + $must_exclude_one_ids = $this->must_exclude_one_ids; + $must_not_contain_ids = $this->must_not_contain_ids; + $must_contain_ids = $this->must_contain_ids; + + /** + * Allow changing the query used for counting for posts using fulltext_native + * + * @event core.search_native_keywords_count_query_before + * @var string search_query The parsed keywords used for this search + * @var array must_not_contain_ids Ids that cannot be taken into account for the results + * @var array must_exclude_one_ids Ids that cannot be on the results + * @var array must_contain_ids Ids that must be on the results + * @var int result_count The previous result count for the format of the query + * Set to 0 to force a re-count + * @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED + * @var array author_ary Array of user_id containing the users to filter the results to + * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant) + * @var array ex_fid_ary Which forums not to search on + * @var int topic_id Limit the search to this topic_id only + * @var string sql_sort_table Extra tables to include in the SQL query. + * Used in conjunction with sql_sort_join + * @var string sql_sort_join SQL conditions to join all the tables used together. + * Used in conjunction with sql_sort_table + * @var int sort_days Time, in days, of the oldest possible post to list + * @var string sql_where An array of the current WHERE clause conditions + * @var string sql_match Which columns to do the search on + * @var string sql_match_where Extra conditions to use to properly filter the matching process + * @var string group_by Whether or not the SQL query requires a GROUP BY for the elements in the SELECT clause + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'search_query', + 'must_not_contain_ids', + 'must_exclude_one_ids', + 'must_contain_ids', + 'result_count', + 'join_topic', + 'author_ary', + 'author_name', + 'ex_fid_ary', + 'topic_id', + 'sql_sort_table', + 'sql_sort_join', + 'sort_days', + 'sql_where', + 'sql_match', + 'sql_match_where', + 'group_by', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_native_keywords_count_query_before', compact($vars))); + if ($topic_id) { $sql_where[] = 'p.topic_id = ' . $topic_id; @@ -975,6 +1045,49 @@ class fulltext_native extends \phpbb\search\base $select = ($type == 'posts') ? 'p.post_id' : 't.topic_id'; $is_mysql = false; + /** + * Allow changing the query used to search for posts by author in fulltext_native + * + * @event core.search_native_author_count_query_before + * @var int total_results The previous result count for the format of the query. + * Set to 0 to force a re-count + * @var string select SQL SELECT clause for what to get + * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen + * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table + * @var array sql_author SQL WHERE condition for the post author ids + * @var int topic_id Limit the search to this topic_id only + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var string sort_days Time, in days, that the oldest post showing can have + * @var string sql_time The SQL to search on the time specifyed by sort_days + * @var bool firstpost_only Wether or not to search only on the first post of the topics + * @var array ex_fid_ary Forum ids that must not be searched on + * @var array sql_fora SQL query for ex_fid_ary + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'total_results', + 'select', + 'sql_sort_table', + 'sql_sort_join', + 'sql_author', + 'topic_id', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'sort_days', + 'sql_time', + 'firstpost_only', + 'ex_fid_ary', + 'sql_fora', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_native_author_count_query_before', compact($vars))); + // If the cache was completely empty count the results if (!$total_results) { @@ -1078,7 +1191,7 @@ class fulltext_native extends \phpbb\search\base // Count rows for the executed queries. Replace $select within $sql with SQL_CALC_FOUND_ROWS, and run it. $sql_calc = str_replace('SELECT ' . $select, 'SELECT DISTINCT SQL_CALC_FOUND_ROWS p.post_id', $sql); - $this->db->sql_query($sql_calc); + $result = $this->db->sql_query($sql_calc); $this->db->sql_freeresult($result); $sql_count = 'SELECT FOUND_ROWS() as total_results'; diff --git a/phpBB/phpbb/search/fulltext_postgres.php b/phpBB/phpbb/search/fulltext_postgres.php index 5a68f0cbfb..8fe80a39a3 100644 --- a/phpBB/phpbb/search/fulltext_postgres.php +++ b/phpBB/phpbb/search/fulltext_postgres.php @@ -56,6 +56,12 @@ class fulltext_postgres extends \phpbb\search\base protected $db; /** + * phpBB event dispatcher object + * @var \phpbb\event\dispatcher_interface + */ + protected $phpbb_dispatcher; + + /** * User object * @var \phpbb\user */ @@ -92,11 +98,13 @@ class fulltext_postgres extends \phpbb\search\base * @param \phpbb\config\config $config Config object * @param \phpbb\db\driver\driver_interface Database object * @param \phpbb\user $user User object + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object */ - public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user) + public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher) { $this->config = $config; $this->db = $db; + $this->phpbb_dispatcher = $phpbb_dispatcher; $this->user = $user; $this->word_length = array('min' => $this->config['fulltext_postgres_min_word_len'], 'max' => $this->config['fulltext_postgres_max_word_len']); @@ -409,6 +417,55 @@ class fulltext_postgres extends \phpbb\search\base break; } + $tsearch_query = $this->tsearch_query; + + /** + * Allow changing the query used to search for posts using fulltext_postgres + * + * @event core.search_postgres_keywords_main_query_before + * @var string tsearch_query The parsed keywords used for this search + * @var int result_count The previous result count for the format of the query. + * Set to 0 to force a re-count + * @var bool join_topic Weather or not TOPICS_TABLE should be CROSS JOIN'ED + * @var array author_ary Array of user_id containing the users to filter the results to + * @var string author_name An extra username to search on (!empty(author_ary) must be true, to be relevant) + * @var array ex_fid_ary Which forums not to search on + * @var int topic_id Limit the search to this topic_id only + * @var string sql_sort_table Extra tables to include in the SQL query. + * Used in conjunction with sql_sort_join + * @var string sql_sort_join SQL conditions to join all the tables used together. + * Used in conjunction with sql_sort_table + * @var int sort_days Time, in days, of the oldest possible post to list + * @var string sql_match Which columns to do the search on. + * @var string sql_match_where Extra conditions to use to properly filter the matching process + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'tsearch_query', + 'result_count', + 'join_topic', + 'author_ary', + 'author_name', + 'ex_fid_ary', + 'topic_id', + 'sql_sort_table', + 'sql_sort_join', + 'sort_days', + 'sql_match', + 'sql_match_where', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_keywords_main_query_before', compact($vars))); + $sql_select = ($type == 'posts') ? 'p.post_id' : 'DISTINCT t.topic_id'; $sql_from = ($join_topic) ? TOPICS_TABLE . ' t, ' : ''; $field = ($type == 'posts') ? 'post_id' : 'topic_id'; @@ -595,6 +652,55 @@ class fulltext_postgres extends \phpbb\search\base $m_approve_fid_sql = ' AND ' . $post_visibility; + /** + * Allow changing the query used to search for posts by author in fulltext_postgres + * + * @event core.search_postgres_author_count_query_before + * @var int result_count The previous result count for the format of the query. + * Set to 0 to force a re-count + * @var string sql_sort_table CROSS JOIN'ed table to allow doing the sort chosen + * @var string sql_sort_join Condition to define how to join the CROSS JOIN'ed table specifyed in sql_sort_table + * @var array author_ary Array of user_id containing the users to filter the results to + * @var string author_name An extra username to search on + * @var string sql_author SQL WHERE condition for the post author ids + * @var int topic_id Limit the search to this topic_id only + * @var string sql_topic_id SQL of topic_id + * @var string sort_by_sql The possible predefined sort types + * @var string sort_key The sort type used from the possible sort types + * @var string sort_dir "a" for ASC or "d" dor DESC for the sort order used + * @var string sql_sort The result SQL when processing sort_by_sql + sort_key + sort_dir + * @var string sort_days Time, in days, that the oldest post showing can have + * @var string sql_time The SQL to search on the time specifyed by sort_days + * @var bool firstpost_only Wether or not to search only on the first post of the topics + * @var array ex_fid_ary Forum ids that must not be searched on + * @var array sql_fora SQL query for ex_fid_ary + * @var string m_approve_fid_sql WHERE clause condition on post_visibility restrictions + * @var int start How many posts to skip in the search results (used for pagination) + * @since 3.1.5-RC1 + */ + $vars = array( + 'result_count', + 'sql_sort_table', + 'sql_sort_join', + 'author_ary', + 'author_name', + 'sql_author', + 'topic_id', + 'sql_topic_id', + 'sort_by_sql', + 'sort_key', + 'sort_dir', + 'sql_sort', + 'sort_days', + 'sql_time', + 'firstpost_only', + 'ex_fid_ary', + 'sql_fora', + 'm_approve_fid_sql', + 'start', + ); + extract($this->phpbb_dispatcher->trigger_event('core.search_postgres_author_count_query_before', compact($vars))); + // Build the query for really selecting the post_ids if ($type == 'posts') { diff --git a/phpBB/phpbb/search/fulltext_sphinx.php b/phpBB/phpbb/search/fulltext_sphinx.php index a5ad96b114..937292fd38 100644 --- a/phpBB/phpbb/search/fulltext_sphinx.php +++ b/phpBB/phpbb/search/fulltext_sphinx.php @@ -96,6 +96,12 @@ class fulltext_sphinx protected $dbtype; /** + * phpBB event dispatcher object + * @var \phpbb\event\dispatcher_interface + */ + protected $phpbb_dispatcher; + + /** * User object * @var \phpbb\user */ @@ -125,12 +131,14 @@ class fulltext_sphinx * @param \phpbb\config\config $config Config object * @param \phpbb\db\driver\driver_interface Database object * @param \phpbb\user $user User object + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object */ - public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user) + public function __construct(&$error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user, $phpbb_dispatcher) { $this->phpbb_root_path = $phpbb_root_path; $this->php_ext = $phpEx; $this->config = $config; + $this->phpbb_dispatcher = $phpbb_dispatcher; $this->user = $user; $this->db = $db; $this->auth = $auth; @@ -139,7 +147,7 @@ class fulltext_sphinx global $phpbb_container; // TODO inject into object $this->db_tools = $phpbb_container->get('dbal.tools'); - if(!$this->config['fulltext_sphinx_id']) + if (!$this->config['fulltext_sphinx_id']) { $this->config->set('fulltext_sphinx_id', unique_id()); } diff --git a/phpBB/phpbb/template/twig/node/definenode.php b/phpBB/phpbb/template/twig/node/definenode.php index c110785c4b..ddbd151d20 100644 --- a/phpBB/phpbb/template/twig/node/definenode.php +++ b/phpBB/phpbb/template/twig/node/definenode.php @@ -14,7 +14,6 @@ namespace phpbb\template\twig\node; - class definenode extends \Twig_Node { public function __construct($capture, \Twig_NodeInterface $name, \Twig_NodeInterface $value, $lineno, $tag = null) diff --git a/phpBB/phpbb/template/twig/node/event.php b/phpBB/phpbb/template/twig/node/event.php index 8fc4ba4775..b765bde98d 100644 --- a/phpBB/phpbb/template/twig/node/event.php +++ b/phpBB/phpbb/template/twig/node/event.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\node; - class event extends \Twig_Node { /** diff --git a/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php b/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php index 8c7f7b378d..2cd15d59da 100644 --- a/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php +++ b/phpBB/phpbb/template/twig/node/expression/binary/equalequal.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\node\expression\binary; - class equalequal extends \Twig_Node_Expression_Binary { public function operator(\Twig_Compiler $compiler) diff --git a/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php b/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php index 2e95c68090..5f2908fb9b 100644 --- a/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php +++ b/phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\node\expression\binary; - class notequalequal extends \Twig_Node_Expression_Binary { public function operator(\Twig_Compiler $compiler) diff --git a/phpBB/phpbb/template/twig/node/includenode.php b/phpBB/phpbb/template/twig/node/includenode.php index 42428b6106..c36ac3c324 100644 --- a/phpBB/phpbb/template/twig/node/includenode.php +++ b/phpBB/phpbb/template/twig/node/includenode.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\node; - class includenode extends \Twig_Node_Include { /** diff --git a/phpBB/phpbb/template/twig/node/includephp.php b/phpBB/phpbb/template/twig/node/includephp.php index 659495fd9e..76182c2f84 100644 --- a/phpBB/phpbb/template/twig/node/includephp.php +++ b/phpBB/phpbb/template/twig/node/includephp.php @@ -14,7 +14,6 @@ namespace phpbb\template\twig\node; - class includephp extends \Twig_Node { /** @var \Twig_Environment */ diff --git a/phpBB/phpbb/template/twig/node/php.php b/phpBB/phpbb/template/twig/node/php.php index 3a24513dca..4ee415e446 100644 --- a/phpBB/phpbb/template/twig/node/php.php +++ b/phpBB/phpbb/template/twig/node/php.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\node; - class php extends \Twig_Node { /** @var \Twig_Environment */ diff --git a/phpBB/phpbb/template/twig/tokenparser/defineparser.php b/phpBB/phpbb/template/twig/tokenparser/defineparser.php index 2b88d61118..b755836ccd 100644 --- a/phpBB/phpbb/template/twig/tokenparser/defineparser.php +++ b/phpBB/phpbb/template/twig/tokenparser/defineparser.php @@ -14,7 +14,6 @@ namespace phpbb\template\twig\tokenparser; - class defineparser extends \Twig_TokenParser { /** diff --git a/phpBB/phpbb/template/twig/tokenparser/event.php b/phpBB/phpbb/template/twig/tokenparser/event.php index 4c7c8e07d9..f73ef4ae25 100644 --- a/phpBB/phpbb/template/twig/tokenparser/event.php +++ b/phpBB/phpbb/template/twig/tokenparser/event.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\tokenparser; - class event extends \Twig_TokenParser { /** diff --git a/phpBB/phpbb/template/twig/tokenparser/includejs.php b/phpBB/phpbb/template/twig/tokenparser/includejs.php index 4156048e42..4b67d2c468 100644 --- a/phpBB/phpbb/template/twig/tokenparser/includejs.php +++ b/phpBB/phpbb/template/twig/tokenparser/includejs.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\tokenparser; - class includejs extends \Twig_TokenParser { /** diff --git a/phpBB/phpbb/template/twig/tokenparser/includeparser.php b/phpBB/phpbb/template/twig/tokenparser/includeparser.php index 6ee78e5562..aa7236aaa6 100644 --- a/phpBB/phpbb/template/twig/tokenparser/includeparser.php +++ b/phpBB/phpbb/template/twig/tokenparser/includeparser.php @@ -14,7 +14,6 @@ namespace phpbb\template\twig\tokenparser; - class includeparser extends \Twig_TokenParser_Include { /** diff --git a/phpBB/phpbb/template/twig/tokenparser/includephp.php b/phpBB/phpbb/template/twig/tokenparser/includephp.php index c09f7729b0..3992636f8c 100644 --- a/phpBB/phpbb/template/twig/tokenparser/includephp.php +++ b/phpBB/phpbb/template/twig/tokenparser/includephp.php @@ -14,7 +14,6 @@ namespace phpbb\template\twig\tokenparser; - class includephp extends \Twig_TokenParser { /** diff --git a/phpBB/phpbb/template/twig/tokenparser/php.php b/phpBB/phpbb/template/twig/tokenparser/php.php index 557a70cca1..f11ce35896 100644 --- a/phpBB/phpbb/template/twig/tokenparser/php.php +++ b/phpBB/phpbb/template/twig/tokenparser/php.php @@ -13,7 +13,6 @@ namespace phpbb\template\twig\tokenparser; - class php extends \Twig_TokenParser { /** diff --git a/phpBB/phpbb/textformatter/parser_interface.php b/phpBB/phpbb/textformatter/parser_interface.php index 3cb9f8e977..ad611fb5b4 100644 --- a/phpBB/phpbb/textformatter/parser_interface.php +++ b/phpBB/phpbb/textformatter/parser_interface.php @@ -82,7 +82,8 @@ interface parser_interface /** * Get the list of errors that were generated during last parsing * - * @return array + * @return array[] Array of arrays. Each array contains a lang string at index 0 plus any number + * of optional parameters */ public function get_errors(); diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index 9576abe1f0..e07a1b52ca 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -68,7 +68,7 @@ class factory implements \phpbb\textformatter\cache_interface 'b' => '[B]{TEXT}[/B]', 'code' => '[CODE]{TEXT}[/CODE]', 'color' => '[COLOR={COLOR}]{TEXT}[/COLOR]', - 'email' => '[EMAIL={EMAIL;useContent}]{TEXT}[/EMAIL]', + 'email' => '[EMAIL={EMAIL;useContent} subject={TEXT;optional;postFilter=rawurlencode} body={TEXT;optional;postFilter=rawurlencode}]{TEXT}[/EMAIL]', 'flash' => '[FLASH={NUMBER1},{NUMBER2} width={NUMBER1;postFilter=#flashwidth} height={NUMBER2;postFilter=#flashheight} url={URL;useContent} /]', 'i' => '[I]{TEXT}[/I]', 'img' => '[IMG src={IMAGEURL;useContent}]', @@ -97,7 +97,18 @@ class factory implements \phpbb\textformatter\cache_interface 'img' => '<img src="{IMAGEURL}" alt="{L_IMAGE}"/>', 'size' => '<span style="font-size: {FONTSIZE}%; line-height: normal"><xsl:apply-templates/></span>', 'color' => '<span style="color: {COLOR}"><xsl:apply-templates/></span>', - 'email' => '<a href="mailto:{EMAIL}"><xsl:apply-templates/></a>', + 'email' => '<a> + <xsl:attribute name="href"> + <xsl:text>mailto:</xsl:text> + <xsl:value-of select="@email"/> + <xsl:if test="@subject or @body"> + <xsl:text>?</xsl:text> + <xsl:if test="@subject">subject=<xsl:value-of select="@subject"/></xsl:if> + <xsl:if test="@body"><xsl:if test="@subject">&</xsl:if>body=<xsl:value-of select="@body"/></xsl:if> + </xsl:if> + </xsl:attribute> + <xsl:apply-templates/> + </a>', ); /** diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php index e46a0578d2..838c211e56 100644 --- a/phpBB/phpbb/textformatter/s9e/parser.php +++ b/phpBB/phpbb/textformatter/s9e/parser.php @@ -32,20 +32,14 @@ class parser implements \phpbb\textformatter\parser_interface protected $parser; /** - * @var \phpbb\user User object, used for translating errors - */ - protected $user; - - /** * Constructor * * @param \phpbb\cache\driver_interface $cache * @param string $key Cache key - * @param \phpbb\user $user * @param factory $factory * @param \phpbb\event\dispatcher_interface $dispatcher */ - public function __construct(\phpbb\cache\driver\driver_interface $cache, $key, \phpbb\user $user, factory $factory, \phpbb\event\dispatcher_interface $dispatcher) + public function __construct(\phpbb\cache\driver\driver_interface $cache, $key, factory $factory, \phpbb\event\dispatcher_interface $dispatcher) { $parser = $cache->get($key); if (!$parser) @@ -56,24 +50,21 @@ class parser implements \phpbb\textformatter\parser_interface $this->dispatcher = $dispatcher; $this->parser = $parser; - $this->user = $user; $parser = $this; /** * Configure the parser service * * Can be used to: - * - toggle features according to the user's preferences, - * - toggle BBCodes according to the user's permissions, - * - register variables or custom parsers in the s9e\TextFormatter - * - configure the s9e\TextFormatter parser + * - toggle features or BBCodes + * - register variables or custom parsers in the s9e\TextFormatter parser + * - configure the s9e\TextFormatter parser's runtime settings * * @event core.text_formatter_s9e_parser_setup * @var \phpbb\textformatter\s9e\parser parser This parser service - * @var \phpbb\user user Current user * @since 3.2.0-a1 */ - $vars = array('parser', 'user'); + $vars = array('parser'); extract($dispatcher->trigger_event('core.text_formatter_s9e_parser_setup', compact($vars))); } @@ -196,13 +187,12 @@ class parser implements \phpbb\textformatter\parser_interface /** * {@inheritdoc} * - * This will translate the log entries found in s9e\TextFormatter's logger into phpBB error + * This will convert the log entries found in s9e\TextFormatter's logger into phpBB error * messages */ public function get_errors() { $errors = array(); - foreach ($this->parser->getLogger()->get() as $entry) { list($type, $msg, $context) = $entry; @@ -211,33 +201,39 @@ class parser implements \phpbb\textformatter\parser_interface { if ($context['tagName'] === 'E') { - $errors[] = $this->user->lang('TOO_MANY_SMILIES', $context['tagLimit']); + $errors[] = array('TOO_MANY_SMILIES', $context['tagLimit']); } else if ($context['tagName'] === 'URL') { - $errors[] = $this->user->lang('TOO_MANY_URLS', $context['tagLimit']); + $errors[] = array('TOO_MANY_URLS', $context['tagLimit']); } } else if ($msg === 'MAX_FONT_SIZE_EXCEEDED') { - $errors[] = $this->user->lang($msg, $context['max_size']); + $errors[] = array($msg, $context['max_size']); } else if (preg_match('/^MAX_(?:FLASH|IMG)_(HEIGHT|WIDTH)_EXCEEDED$/D', $msg, $m)) { - $errors[] = $this->user->lang($msg, $context['max_' . strtolower($m[1])]); + $errors[] = array($msg, $context['max_' . strtolower($m[1])]); } else if ($msg === 'Tag is disabled') { $name = strtolower($context['tag']->getName()); - $errors[] = $this->user->lang('UNAUTHORISED_BBCODE', '[' . $name . ']'); + $errors[] = array('UNAUTHORISED_BBCODE', '[' . $name . ']'); } else if ($msg === 'UNABLE_GET_IMAGE_SIZE') { - $errors[] = $this->user->lang($msg); + $errors[] = array($msg); } } - return array_unique($errors); + // Deduplicate error messages. array_unique() only works on strings so we have to serialize + if (!empty($errors)) + { + $errors = array_map('unserialize', array_unique(array_map('serialize', $errors))); + } + + return $errors; } /** diff --git a/phpBB/phpbb/textformatter/s9e/renderer.php b/phpBB/phpbb/textformatter/s9e/renderer.php index 8999f1d25f..51bc44f339 100644 --- a/phpBB/phpbb/textformatter/s9e/renderer.php +++ b/phpBB/phpbb/textformatter/s9e/renderer.php @@ -234,10 +234,6 @@ class renderer implements \phpbb\textformatter\renderer_interface } $html = $this->renderer->render($xml); - if (stripos($html, '<code') !== false) - { - $html = $this->replace_tabs_in_code($html); - } /** * Modify a rendered text @@ -254,45 +250,6 @@ class renderer implements \phpbb\textformatter\renderer_interface } /** - * Replace tabs in code elements - * - * @see bbcode::bbcode_second_pass_code() - * - * @param string $html Original HTML - * @return string Modified HTML - */ - protected function replace_tabs_in_code($html) - { - return preg_replace_callback( - '((<code[^>]*>)(.*?)(</code>))is', - function ($captures) - { - $code = $captures[2]; - - $code = str_replace("\t", ' ', $code); - $code = str_replace(' ', ' ', $code); - $code = str_replace(' ', ' ', $code); - $code = str_replace("\n ", "\n ", $code); - - // keep space at the beginning - if (!empty($code) && $code[0] == ' ') - { - $code = ' ' . substr($code, 1); - } - - // remove newline at the beginning - if (!empty($code) && $code[0] == "\n") - { - $code = substr($code, 1); - } - - return $captures[1] . $code . $captures[3]; - }, - $html - ); - } - - /** * {@inheritdoc} */ public function set_smilies_path($path) diff --git a/phpBB/phpbb/textformatter/s9e/utils.php b/phpBB/phpbb/textformatter/s9e/utils.php index 2018bbf519..803c71a5a2 100644 --- a/phpBB/phpbb/textformatter/s9e/utils.php +++ b/phpBB/phpbb/textformatter/s9e/utils.php @@ -35,6 +35,72 @@ class utils implements \phpbb\textformatter\utils_interface } /** + * Return given string between quotes + * + * Will use either single- or double- quotes depending on whichever requires less escaping. + * Quotes and backslashes are escaped with backslashes where necessary + * + * @param string $str Original string + * @return string Escaped string within quotes + */ + protected function enquote($str) + { + $singleQuoted = "'" . addcslashes($str, "\\'") . "'"; + $doubleQuoted = '"' . addcslashes($str, '\\"') . '"'; + + return (strlen($singleQuoted) < strlen($doubleQuoted)) ? $singleQuoted : $doubleQuoted; + } + + /** + * {@inheritdoc} + */ + public function generate_quote($text, array $attributes = array()) + { + $text = trim($text); + $quote = '[quote'; + if (isset($attributes['author'])) + { + // Add the author as the BBCode's default attribute + $quote .= '=' . $this->enquote($attributes['author']); + unset($attributes['author']); + } + foreach ($attributes as $name => $value) + { + $quote .= ' ' . $name . '=' . $this->enquote($value); + } + $quote .= ']'; + $newline = (strlen($quote . $text . '[/quote]') > 80 || strpos($text, "\n") !== false) ? "\n" : ''; + $quote .= $newline . $text . $newline . '[/quote]'; + + return $quote; + } + + /** + * Get a list of quote authors, limited to the outermost quotes + * + * @param string $xml Parsed text + * @return string[] List of authors + */ + public function get_outermost_quote_authors($xml) + { + $authors = array(); + if (strpos($xml, '<QUOTE ') === false) + { + return $authors; + } + + $dom = new \DOMDocument; + $dom->loadXML($xml); + $xpath = new \DOMXPath($dom); + foreach ($xpath->query('//QUOTE[not(ancestor::QUOTE)]/@author') as $author) + { + $authors[] = $author->textContent; + } + + return $authors; + } + + /** * Remove given BBCode and its content, at given nesting depth * * @param string $xml Parsed text diff --git a/phpBB/phpbb/textformatter/utils_interface.php b/phpBB/phpbb/textformatter/utils_interface.php index 132dc8ece4..41a6ba2345 100644 --- a/phpBB/phpbb/textformatter/utils_interface.php +++ b/phpBB/phpbb/textformatter/utils_interface.php @@ -29,6 +29,26 @@ interface utils_interface public function clean_formatting($text); /** + * Create a quote block for given text + * + * Possible attributes: + * - author + * + * @param string $text Quote's text + * @param array $attributes Quote's attributes + * @return string Quote block to be used in a new post/text + */ + public function generate_quote($text, array $attributes = array()); + + /** + * Get a list of quote authors, limited to the outermost quotes + * + * @param string $text Parsed text + * @return string[] List of authors + */ + public function get_outermost_quote_authors($text); + + /** * Remove given BBCode and its content, at given nesting depth * * @param string $text Parsed text diff --git a/phpBB/phpbb/tree/nestedset.php b/phpBB/phpbb/tree/nestedset.php index 57d109652e..8490c7c299 100644 --- a/phpBB/phpbb/tree/nestedset.php +++ b/phpBB/phpbb/tree/nestedset.php @@ -837,7 +837,10 @@ abstract class nestedset implements \phpbb\tree\tree_interface ' . $this->get_sql_where('AND') . ' ORDER BY ' . $this->column_left_id . ', ' . $this->column_item_id . ' ASC'; $result = $this->db->sql_query($sql); - while ($row = $this->db->sql_fetchrow($result)) + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + foreach ($rows as $row) { // First we update the left_id for this module if ($row[$this->column_left_id] != $new_id) @@ -862,7 +865,6 @@ abstract class nestedset implements \phpbb\tree\tree_interface } $new_id++; } - $this->db->sql_freeresult($result); if ($acquired_new_lock) { diff --git a/phpBB/phpbb/user_loader.php b/phpBB/phpbb/user_loader.php index 0b192e4452..cdd28329db 100644 --- a/phpBB/phpbb/user_loader.php +++ b/phpBB/phpbb/user_loader.php @@ -179,9 +179,10 @@ class user_loader * @param bool $query Should we query the database if this user has not yet been loaded? * Typically this should be left as false and you should make sure * you load users ahead of time with load_users() + * @param bool @lazy If true, will be lazy loaded (requires JS) * @return string */ - public function get_avatar($user_id, $query = false) + public function get_avatar($user_id, $query = false, $lazy = false) { if (!($user = $this->get_user($user_id, $query))) { @@ -195,7 +196,7 @@ class user_loader 'avatar_height' => $user['user_avatar_height'], ); - return phpbb_get_avatar($row, 'USER_AVATAR'); + return phpbb_get_avatar($row, 'USER_AVATAR', false, $lazy); } /** |