aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/phpbb
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/phpbb')
-rw-r--r--phpBB/phpbb/auth/provider/base.php2
-rw-r--r--phpBB/phpbb/auth/provider/ldap.php3
-rw-r--r--phpBB/phpbb/auth/provider/oauth/oauth.php9
-rw-r--r--phpBB/phpbb/auth/provider/oauth/token_storage.php1
-rw-r--r--phpBB/phpbb/auth/provider/provider_interface.php6
-rw-r--r--phpBB/phpbb/cache/driver/memcache.php2
-rw-r--r--phpBB/phpbb/cron/task/core/tidy_search.php51
-rw-r--r--phpBB/phpbb/db/extractor/mssql_extractor.php2
-rw-r--r--phpBB/phpbb/db/migration/data/v310/acp_prune_users_module.php11
-rw-r--r--phpBB/phpbb/db/migration/data/v310/dev.php11
-rw-r--r--phpBB/phpbb/db/migration/data/v310/style_update_p1.php8
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315.php31
-rw-r--r--phpBB/phpbb/db/migration/data/v31x/v315rc1.php31
-rw-r--r--phpBB/phpbb/db/migration/tool/module.php55
-rw-r--r--phpBB/phpbb/db/migration/tool/permission.php22
-rw-r--r--phpBB/phpbb/db/sql_insert_buffer.php2
-rw-r--r--phpBB/phpbb/di/container_builder.php499
-rw-r--r--phpBB/phpbb/di/extension/core.php6
-rw-r--r--phpBB/phpbb/event/md_exporter.php89
-rw-r--r--phpBB/phpbb/event/php_exporter.php1
-rw-r--r--phpBB/phpbb/help/controller/bbcode.php85
-rw-r--r--phpBB/phpbb/help/controller/controller.php76
-rw-r--r--phpBB/phpbb/help/controller/faq.php165
-rw-r--r--phpBB/phpbb/help/manager.php136
-rw-r--r--phpBB/phpbb/language/language.php7
-rw-r--r--phpBB/phpbb/language/language_file_loader.php12
-rw-r--r--phpBB/phpbb/log/log.php85
-rw-r--r--phpBB/phpbb/module/exception/module_exception.php19
-rw-r--r--phpBB/phpbb/module/exception/module_not_found_exception.php19
-rw-r--r--phpBB/phpbb/module/module_manager.php564
-rw-r--r--phpBB/phpbb/notification/method/base.php4
-rw-r--r--phpBB/phpbb/notification/type/admin_activate_user.php2
-rw-r--r--phpBB/phpbb/notification/type/group_request.php2
-rw-r--r--phpBB/phpbb/notification/type/pm.php2
-rw-r--r--phpBB/phpbb/notification/type/post.php2
-rw-r--r--phpBB/phpbb/notification/type/quote.php31
-rw-r--r--phpBB/phpbb/notification/type/report_pm.php2
-rw-r--r--phpBB/phpbb/notification/type/report_pm_closed.php2
-rw-r--r--phpBB/phpbb/notification/type/report_post.php2
-rw-r--r--phpBB/phpbb/notification/type/report_post_closed.php2
-rw-r--r--phpBB/phpbb/notification/type/topic.php2
-rw-r--r--phpBB/phpbb/passwords/driver/helper.php16
-rw-r--r--phpBB/phpbb/routing/router.php190
-rw-r--r--phpBB/phpbb/search/fulltext_mysql.php108
-rw-r--r--phpBB/phpbb/search/fulltext_native.php117
-rw-r--r--phpBB/phpbb/search/fulltext_postgres.php108
-rw-r--r--phpBB/phpbb/search/fulltext_sphinx.php12
-rw-r--r--phpBB/phpbb/template/twig/node/definenode.php1
-rw-r--r--phpBB/phpbb/template/twig/node/event.php1
-rw-r--r--phpBB/phpbb/template/twig/node/expression/binary/equalequal.php1
-rw-r--r--phpBB/phpbb/template/twig/node/expression/binary/notequalequal.php1
-rw-r--r--phpBB/phpbb/template/twig/node/includenode.php1
-rw-r--r--phpBB/phpbb/template/twig/node/includephp.php1
-rw-r--r--phpBB/phpbb/template/twig/node/php.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/defineparser.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/event.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includejs.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includeparser.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/includephp.php1
-rw-r--r--phpBB/phpbb/template/twig/tokenparser/php.php1
-rw-r--r--phpBB/phpbb/textformatter/parser_interface.php3
-rw-r--r--phpBB/phpbb/textformatter/s9e/factory.php15
-rw-r--r--phpBB/phpbb/textformatter/s9e/parser.php42
-rw-r--r--phpBB/phpbb/textformatter/s9e/renderer.php43
-rw-r--r--phpBB/phpbb/textformatter/s9e/utils.php66
-rw-r--r--phpBB/phpbb/textformatter/utils_interface.php20
-rw-r--r--phpBB/phpbb/tree/nestedset.php6
-rw-r--r--phpBB/phpbb/user_loader.php5
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=&quot;(.+?)&quot;#';
-
- /**
* 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">&amp;</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", '&nbsp; &nbsp;', $code);
- $code = str_replace(' ', '&nbsp; ', $code);
- $code = str_replace(' ', ' &nbsp;', $code);
- $code = str_replace("\n ", "\n&nbsp;", $code);
-
- // keep space at the beginning
- if (!empty($code) && $code[0] == ' ')
- {
- $code = '&nbsp;' . 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);
}
/**