aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes
diff options
context:
space:
mode:
Diffstat (limited to 'phpBB/includes')
-rw-r--r--phpBB/includes/acp/acp_captcha.php4
-rw-r--r--phpBB/includes/acp/acp_modules.php103
-rw-r--r--phpBB/includes/acp/acp_search.php43
-rw-r--r--phpBB/includes/bbcode.php5
-rw-r--r--phpBB/includes/captcha/captcha_factory.php42
-rw-r--r--phpBB/includes/captcha/plugins/captcha_abstract.php9
-rw-r--r--phpBB/includes/class_loader.php42
-rw-r--r--phpBB/includes/constants.php1
-rw-r--r--phpBB/includes/cron/manager.php121
-rw-r--r--phpBB/includes/cron/task/provider.php48
-rw-r--r--phpBB/includes/extension/base.php57
-rw-r--r--phpBB/includes/extension/finder.php417
-rw-r--r--phpBB/includes/extension/interface.php65
-rw-r--r--phpBB/includes/extension/manager.php415
-rw-r--r--phpBB/includes/extension/provider.php68
-rw-r--r--phpBB/includes/functions_messenger.php5
-rw-r--r--phpBB/includes/functions_module.php180
-rw-r--r--phpBB/includes/functions_posting.php9
-rw-r--r--phpBB/includes/search/base.php (renamed from phpBB/includes/search/search.php)4
-rw-r--r--phpBB/includes/search/fulltext_mysql.php19
-rw-r--r--phpBB/includes/search/fulltext_native.php22
-rw-r--r--phpBB/includes/session.php68
-rw-r--r--phpBB/includes/template/extension_path_provider.php130
-rw-r--r--phpBB/includes/template/locator.php97
-rw-r--r--phpBB/includes/template/path_provider.php102
-rw-r--r--phpBB/includes/template/path_provider_interface.php54
-rw-r--r--phpBB/includes/template/template.php71
27 files changed, 1769 insertions, 432 deletions
diff --git a/phpBB/includes/acp/acp_captcha.php b/phpBB/includes/acp/acp_captcha.php
index bef8ae0ea9..f051781547 100644
--- a/phpBB/includes/acp/acp_captcha.php
+++ b/phpBB/includes/acp/acp_captcha.php
@@ -104,13 +104,13 @@ class acp_captcha
foreach ($captchas['available'] as $value => $title)
{
$current = ($selected !== false && $value == $selected) ? ' selected="selected"' : '';
- $captcha_select .= '<option value="' . $value . '"' . $current . '>' . $user->lang[$title] . '</option>';
+ $captcha_select .= '<option value="' . $value . '"' . $current . '>' . $user->lang($title) . '</option>';
}
foreach ($captchas['unavailable'] as $value => $title)
{
$current = ($selected !== false && $value == $selected) ? ' selected="selected"' : '';
- $captcha_select .= '<option value="' . $value . '"' . $current . ' class="disabled-option">' . $user->lang[$title] . '</option>';
+ $captcha_select .= '<option value="' . $value . '"' . $current . ' class="disabled-option">' . $user->lang($title) . '</option>';
}
$demo_captcha = phpbb_captcha_factory::get_instance($selected);
diff --git a/phpBB/includes/acp/acp_modules.php b/phpBB/includes/acp/acp_modules.php
index 52033b590c..e51b440d4d 100644
--- a/phpBB/includes/acp/acp_modules.php
+++ b/phpBB/includes/acp/acp_modules.php
@@ -111,7 +111,7 @@ class acp_modules
}
break;
-
+
case 'enable':
case 'disable':
if (!$module_id)
@@ -170,7 +170,7 @@ class acp_modules
add_log('admin', 'LOG_MODULE_' . strtoupper($action), $this->lang_name($row['module_langname']), $move_module_name);
$this->remove_cache_file();
}
-
+
break;
case 'quickadd':
@@ -207,7 +207,7 @@ class acp_modules
if (!sizeof($errors))
{
$this->remove_cache_file();
-
+
trigger_error($user->lang['MODULE_ADDED'] . adm_back_link($this->u_action . '&amp;parent_id=' . $this->parent_id));
}
}
@@ -231,7 +231,7 @@ class acp_modules
{
trigger_error($user->lang['NO_MODULE_ID'] . adm_back_link($this->u_action . '&amp;parent_id=' . $this->parent_id), E_USER_WARNING);
}
-
+
$module_row = $this->get_module_row($module_id);
// no break
@@ -250,7 +250,7 @@ class acp_modules
'module_auth' => '',
);
}
-
+
$module_data = array();
$module_data['module_basename'] = request_var('module_basename', (string) $module_row['module_basename']);
@@ -295,7 +295,7 @@ class acp_modules
if (!sizeof($errors))
{
$this->remove_cache_file();
-
+
trigger_error((($action == 'add') ? $user->lang['MODULE_ADDED'] : $user->lang['MODULE_EDITED']) . adm_back_link($this->u_action . '&amp;parent_id=' . $this->parent_id));
}
}
@@ -316,7 +316,7 @@ class acp_modules
}
// Name options
- $s_name_options .= '<option value="' . $option . '"' . (($option == $module_data['module_basename']) ? ' selected="selected"' : '') . '>' . $this->lang_name($values['title']) . ' [' . $this->module_class . '_' . $option . ']</option>';
+ $s_name_options .= '<option value="' . $option . '"' . (($option == $module_data['module_basename']) ? ' selected="selected"' : '') . '>' . $this->lang_name($values['title']) . ' [' . $option . ']</option>';
$template->assign_block_vars('m_names', array('NAME' => $option, 'A_NAME' => addslashes($option)));
@@ -327,7 +327,7 @@ class acp_modules
{
$s_mode_options .= '<option value="' . $m_mode . '"' . (($m_mode == $module_data['module_mode']) ? ' selected="selected"' : '') . '>' . $this->lang_name($m_values['title']) . '</option>';
}
-
+
$template->assign_block_vars('m_names.modes', array(
'OPTION' => $m_mode,
'VALUE' => $this->lang_name($m_values['title']),
@@ -336,7 +336,7 @@ class acp_modules
);
}
}
-
+
$s_cat_option = '<option value="0"' . (($module_data['parent_id'] == 0) ? ' selected="selected"' : '') . '>' . $user->lang['NO_PARENT'] . '</option>';
$template->assign_vars(array_merge(array(
@@ -349,7 +349,7 @@ class acp_modules
'U_EDIT_ACTION' => $this->u_action . '&amp;parent_id=' . $this->parent_id,
'L_TITLE' => $user->lang[strtoupper($action) . '_MODULE'],
-
+
'MODULENAME' => $this->lang_name($module_data['module_langname']),
'ACTION' => $action,
'MODULE_ID' => $module_id,
@@ -480,7 +480,7 @@ class acp_modules
foreach ($module_infos as $option => $values)
{
// Name options
- $s_install_options .= '<optgroup label="' . $this->lang_name($values['title']) . ' [' . $this->module_class . '_' . $option . ']">';
+ $s_install_options .= '<optgroup label="' . $this->lang_name($values['title']) . ' [' . $option . ']">';
// Build module modes
foreach ($values['modes'] as $m_mode => $m_values)
@@ -516,7 +516,7 @@ class acp_modules
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
-
+
if (!$row)
{
trigger_error($user->lang['NO_MODULE'] . adm_back_link($this->u_action . '&amp;parent_id=' . $this->parent_id), E_USER_WARNING);
@@ -524,14 +524,14 @@ class acp_modules
return $row;
}
-
+
/**
* Get available module information from module files
*/
function get_module_infos($module = '', $module_class = false)
{
global $phpbb_root_path, $phpEx;
-
+
$module_class = ($module_class === false) ? $this->module_class : $module_class;
$directory = $phpbb_root_path . 'includes/' . $module_class . '/info/';
@@ -539,57 +539,72 @@ class acp_modules
if (!$module)
{
- $dh = @opendir($directory);
+ global $phpbb_extension_manager;
- if (!$dh)
- {
- return $fileinfo;
- }
+ $finder = $phpbb_extension_manager->get_finder();
+
+ $modules = $finder
+ ->extension_suffix('_module')
+ ->extension_directory("/$module_class")
+ ->core_path("includes/$module_class/info/")
+ ->core_prefix($module_class . '_')
+ ->get_classes();
- while (($file = readdir($dh)) !== false)
+ foreach ($modules as $module)
{
- // Is module?
- if (preg_match('/^' . $module_class . '_.+\.' . $phpEx . '$/', $file))
- {
- $class = str_replace(".$phpEx", '', $file) . '_info';
+ $info_class = preg_replace('/_module$/', '_info', $module);
- if (!class_exists($class))
+ // 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
+ if (!class_exists($info_class))
+ {
+ $info_class = str_replace("phpbb_{$module_class}_info_", '', $module) . '_info';
+ if (file_exists($directory . $info_class . '.' . $phpEx))
{
- include($directory . $file);
+ include($directory . $info_class . '.' . $phpEx);
}
+ }
- // Get module title tag
- if (class_exists($class))
- {
- $c_class = new $class();
- $module_info = $c_class->module();
- $fileinfo[str_replace($module_class . '_', '', $module_info['filename'])] = $module_info;
- }
+ if (class_exists($info_class))
+ {
+ $info = new $info_class();
+ $module_info = $info->module();
+
+ $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module;
+
+ $fileinfo[$main_class] = $module_info;
}
}
- closedir($dh);
ksort($fileinfo);
}
else
{
- $filename = $module_class . '_' . basename($module);
- $class = $module_class . '_' . basename($module) . '_info';
+ $info_class = preg_replace('/_module$/', '_info', $module);
- if (!class_exists($class))
+ if (!class_exists($info_class))
{
- include($directory . $filename . '.' . $phpEx);
+ if (file_exists($directory . $module . '.' . $phpEx))
+ {
+ include($directory . $module . '.' . $phpEx);
+ }
+ $info_class = $module . '_info';
}
// Get module title tag
- if (class_exists($class))
+ if (class_exists($info_class))
{
- $c_class = new $class();
- $module_info = $c_class->module();
- $fileinfo[str_replace($module_class . '_', '', $module_info['filename'])] = $module_info;
+ $info = new $info_class();
+ $module_info = $info->module();
+
+ $main_class = (isset($module_info['filename'])) ? $module_info['filename'] : $module;
+
+ $fileinfo[$main_class] = $module_info;
}
}
-
+
return $fileinfo;
}
@@ -721,7 +736,7 @@ class acp_modules
// Sanitise for future path use, it's escaped as appropriate for queries
$p_class = str_replace(array('.', '/', '\\'), '', basename($this->module_class));
-
+
$cache->destroy('_modules_' . $p_class);
// Additionally remove sql cache
diff --git a/phpBB/includes/acp/acp_search.php b/phpBB/includes/acp/acp_search.php
index a3061ae2da..ab604cd7cd 100644
--- a/phpBB/includes/acp/acp_search.php
+++ b/phpBB/includes/acp/acp_search.php
@@ -77,7 +77,8 @@ class acp_search
continue;
}
- $name = ucfirst(strtolower(str_replace('_', ' ', $type)));
+ $name = $search->get_name();
+
$selected = ($config['search_type'] == $type) ? ' selected="selected"' : '';
$search_options .= '<option value="' . $type . '"' . $selected . '>' . $name . '</option>';
@@ -275,7 +276,7 @@ class acp_search
{
trigger_error($error . adm_back_link($this->u_action), E_USER_WARNING);
}
- $name = ucfirst(strtolower(str_replace('_', ' ', $this->state[0])));
+ $name = $this->search->get_name();
$action = &$this->state[1];
@@ -454,7 +455,7 @@ class acp_search
continue;
}
- $name = ucfirst(strtolower(str_replace('_', ' ', $type)));
+ $name = $search->get_name();
$data = array();
if (method_exists($search, 'index_stats'))
@@ -553,27 +554,15 @@ class acp_search
function get_search_types()
{
- global $phpbb_root_path, $phpEx;
-
- $search_types = array();
-
- $dp = @opendir($phpbb_root_path . 'includes/search');
-
- if ($dp)
- {
- while (($file = readdir($dp)) !== false)
- {
- if ((preg_match('#\.' . $phpEx . '$#', $file)) && ($file != "search.$phpEx"))
- {
- $search_types[] = preg_replace('#^(.*?)\.' . $phpEx . '$#', '\1', $file);
- }
- }
- closedir($dp);
+ global $phpbb_root_path, $phpEx, $phpbb_extension_manager;
- sort($search_types);
- }
+ $finder = $phpbb_extension_manager->get_finder();
- return $search_types;
+ return $finder
+ ->extension_suffix('_backend')
+ ->extension_directory('/search')
+ ->core_path('includes/search/')
+ ->get_classes();
}
function get_max_post_id()
@@ -610,15 +599,7 @@ class acp_search
{
global $phpbb_root_path, $phpEx, $user;
- if (!preg_match('#^\w+$#', $type) || !file_exists("{$phpbb_root_path}includes/search/$type.$phpEx"))
- {
- $error = $user->lang['NO_SUCH_SEARCH_MODULE'];
- return $error;
- }
-
- include_once("{$phpbb_root_path}includes/search/$type.$phpEx");
-
- if (!class_exists($type))
+ if (!class_exists($type) || !method_exists($type, 'get_name'))
{
$error = $user->lang['NO_SUCH_SEARCH_MODULE'];
return $error;
diff --git a/phpBB/includes/bbcode.php b/phpBB/includes/bbcode.php
index c3367fbd46..929ea45c9b 100644
--- a/phpBB/includes/bbcode.php
+++ b/phpBB/includes/bbcode.php
@@ -127,14 +127,15 @@ class bbcode
*/
function bbcode_cache_init()
{
- global $phpbb_root_path, $phpEx, $config, $user;
+ global $phpbb_root_path, $phpEx, $config, $user, $phpbb_extension_manager;
if (empty($this->template_filename))
{
$this->template_bitfield = new bitfield($user->theme['bbcode_bitfield']);
$template_locator = new phpbb_template_locator();
- $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator);
+ $template_path_provider = new phpbb_template_extension_path_provider($phpbb_extension_manager, new phpbb_template_path_provider());
+ $template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator, $template_path_provider);
$template->set_template();
$template_locator->set_filenames(array('bbcode.html' => 'bbcode.html'));
$this->template_filename = $template_locator->get_source_file_for_handle('bbcode.html');
diff --git a/phpBB/includes/captcha/captcha_factory.php b/phpBB/includes/captcha/captcha_factory.php
index c2ec8c5bda..e039e92054 100644
--- a/phpBB/includes/captcha/captcha_factory.php
+++ b/phpBB/includes/captcha/captcha_factory.php
@@ -59,38 +59,38 @@ class phpbb_captcha_factory
*/
function get_captcha_types()
{
- global $phpbb_root_path, $phpEx;
+ global $phpbb_root_path, $phpEx, $phpbb_extension_manager;
$captchas = array(
'available' => array(),
'unavailable' => array(),
);
- $dp = @opendir($phpbb_root_path . 'includes/captcha/plugins');
+ $finder = $phpbb_extension_manager->get_finder();
+ $captcha_plugin_classes = $finder
+ ->extension_directory('/captcha')
+ ->suffix('_plugin')
+ ->core_path('includes/captcha/plugins/')
+ ->get_classes();
- if ($dp)
+ foreach ($captcha_plugin_classes as $class)
{
- while (($file = readdir($dp)) !== false)
+ // check if this class needs to be loaded in legacy mode
+ $old_class = preg_replace('/^phpbb_captcha_plugins_/', '', $class);
+ if (file_exists($phpbb_root_path . "includes/captcha/plugins/$old_class.$phpEx") && !class_exists($old_class))
{
- if ((preg_match('#_plugin\.' . $phpEx . '$#', $file)))
- {
- $name = preg_replace('#^(.*?)_plugin\.' . $phpEx . '$#', '\1', $file);
- if (!class_exists($name))
- {
- include($phpbb_root_path . "includes/captcha/plugins/$file");
- }
+ include($phpbb_root_path . "includes/captcha/plugins/$old_class.$phpEx");
+ $class = preg_replace('/_plugin$/', '', $old_class);
+ }
- if (call_user_func(array($name, 'is_available')))
- {
- $captchas['available'][$name] = call_user_func(array($name, 'get_name'));
- }
- else
- {
- $captchas['unavailable'][$name] = call_user_func(array($name, 'get_name'));
- }
- }
+ if (call_user_func(array($class, 'is_available')))
+ {
+ $captchas['available'][$class] = call_user_func(array($class, 'get_name'));
+ }
+ else
+ {
+ $captchas['unavailable'][$class] = call_user_func(array($class, 'get_name'));
}
- closedir($dp);
}
return $captchas;
diff --git a/phpBB/includes/captcha/plugins/captcha_abstract.php b/phpBB/includes/captcha/plugins/captcha_abstract.php
index aea39b3123..07a0ea1279 100644
--- a/phpBB/includes/captcha/plugins/captcha_abstract.php
+++ b/phpBB/includes/captcha/plugins/captcha_abstract.php
@@ -22,7 +22,7 @@ if (!defined('IN_PHPBB'))
*
* @package VC
*/
-class phpbb_default_captcha
+class phpbb_captcha_plugins_captcha_abstract
{
var $confirm_id;
var $confirm_code;
@@ -364,3 +364,10 @@ class phpbb_default_captcha
}
}
+
+/**
+* Old class name for legacy use. The new class name is auto loadable.
+*/
+class phpbb_default_captcha extends phpbb_captcha_plugins_captcha_abstract
+{
+}
diff --git a/phpBB/includes/class_loader.php b/phpBB/includes/class_loader.php
index a28d745983..bc268d342e 100644
--- a/phpBB/includes/class_loader.php
+++ b/phpBB/includes/class_loader.php
@@ -31,22 +31,32 @@ if (!defined('IN_PHPBB'))
*/
class phpbb_class_loader
{
- private $phpbb_root_path;
+ private $prefix;
+ private $path;
private $php_ext;
private $cache;
+
+ /**
+ * A map of looked up class names to paths relative to $this->path.
+ * This map is stored in cache and looked up if the cache is available.
+ *
+ * @var array
+ */
private $cached_paths = array();
/**
* Creates a new phpbb_class_loader, which loads files with the given
- * file extension from the given phpbb root path.
+ * file extension from the given path.
*
- * @param string $phpbb_root_path phpBB's root directory containing includes/
- * @param string $php_ext The file extension for PHP files
+ * @param string $prefix Required class name prefix for files to be loaded
+ * @param string $path Directory to load files from
+ * @param string $php_ext The file extension for PHP files
* @param phpbb_cache_driver_interface $cache An implementation of the phpBB cache interface.
*/
- public function __construct($phpbb_root_path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null)
+ public function __construct($prefix, $path, $php_ext = '.php', phpbb_cache_driver_interface $cache = null)
{
- $this->phpbb_root_path = $phpbb_root_path;
+ $this->prefix = $prefix;
+ $this->path = $path;
$this->php_ext = $php_ext;
$this->set_cache($cache);
@@ -63,7 +73,7 @@ class phpbb_class_loader
{
if ($cache)
{
- $this->cached_paths = $cache->get('class_loader');
+ $this->cached_paths = $cache->get('class_loader_' . $this->prefix);
if ($this->cached_paths === false)
{
@@ -100,23 +110,21 @@ class phpbb_class_loader
*/
public function resolve_path($class)
{
- $path_prefix = $this->phpbb_root_path . 'includes/';
-
if (isset($this->cached_paths[$class]))
{
- return $path_prefix . $this->cached_paths[$class] . $this->php_ext;
+ return $this->path . $this->cached_paths[$class] . $this->php_ext;
}
- if (!preg_match('/phpbb_[a-zA-Z0-9_]+/', $class))
+ if (!preg_match('/^' . $this->prefix . '[a-zA-Z0-9_]+$/', $class))
{
return false;
}
- $parts = explode('_', substr($class, 6));
+ $parts = explode('_', substr($class, strlen($this->prefix)));
$dirs = '';
- for ($i = 0, $n = sizeof($parts); $i < $n && is_dir($path_prefix . $dirs . $parts[$i]); $i++)
+ for ($i = 0, $n = sizeof($parts); $i < $n && is_dir($this->path . $dirs . $parts[$i]); $i++)
{
$dirs .= $parts[$i] . '/';
}
@@ -129,7 +137,7 @@ class phpbb_class_loader
$relative_path = $dirs . implode(array_slice($parts, $i, sizeof($parts) - $i), '_');
- if (!file_exists($path_prefix . $relative_path . $this->php_ext))
+ if (!file_exists($this->path . $relative_path . $this->php_ext))
{
return false;
}
@@ -137,10 +145,10 @@ class phpbb_class_loader
if ($this->cache)
{
$this->cached_paths[$class] = $relative_path;
- $this->cache->put('class_loader', $this->cached_paths);
+ $this->cache->put('class_loader_' . $this->prefix, $this->cached_paths);
}
- return $path_prefix . $relative_path . $this->php_ext;
+ return $this->path . $relative_path . $this->php_ext;
}
/**
@@ -150,7 +158,7 @@ class phpbb_class_loader
*/
public function load_class($class)
{
- if (substr($class, 0, 6) === 'phpbb_')
+ if (substr($class, 0, strlen($this->prefix)) === $this->prefix)
{
$path = $this->resolve_path($class);
diff --git a/phpBB/includes/constants.php b/phpBB/includes/constants.php
index 8ef1a4655d..d5b398b7bf 100644
--- a/phpBB/includes/constants.php
+++ b/phpBB/includes/constants.php
@@ -226,6 +226,7 @@ define('CONFIG_TABLE', $table_prefix . 'config');
define('CONFIRM_TABLE', $table_prefix . 'confirm');
define('DISALLOW_TABLE', $table_prefix . 'disallow');
define('DRAFTS_TABLE', $table_prefix . 'drafts');
+define('EXT_TABLE', $table_prefix . 'ext');
define('EXTENSIONS_TABLE', $table_prefix . 'extensions');
define('EXTENSION_GROUPS_TABLE', $table_prefix . 'extension_groups');
define('FORUMS_TABLE', $table_prefix . 'forums');
diff --git a/phpBB/includes/cron/manager.php b/phpBB/includes/cron/manager.php
index 31be1a69cb..a0bf018b33 100644
--- a/phpBB/includes/cron/manager.php
+++ b/phpBB/includes/cron/manager.php
@@ -33,137 +33,24 @@ class phpbb_cron_manager
protected $tasks = array();
/**
- * Path to the root of directory tree with tasks.
- * For bundled phpBB tasks, this is the path to includes/cron/tasks
- * under phpBB root.
- * @var string
- */
- protected $task_path;
-
- /**
- * PHP file extension
- * @var string
- */
- protected $phpEx;
-
- /**
- * Cache driver
- * @var phpbb_cache_driver_interface
- */
- protected $cache;
-
- /**
* Constructor. Loads all available tasks.
*
- * Tasks will be looked up in directory tree rooted at $task_path.
- * Task classes will be autoloaded and must be named according to
- * autoloading naming conventions. To load cron tasks shipped with
- * phpbb, pass $phpbb_root_path . 'includes/cron/task' as $task_path.
- *
- * If $cache is given, names of found cron tasks will be cached in it
- * for one hour. Note that the cron task names are stored without
- * namespacing; if two different phbb_cron_manager instances are
- * constructed with different $task_path arguments but the same $cache,
- * the second instance will use task names found by the first instance.
- *
- * @param string $task_path Directory containing cron tasks
- * @param string $phpEx PHP file extension
- * @param phpbb_cache_driver_interface $cache Cache for task names (optional)
- * @return void
+ * @param array|Traversable $task_names Provides an iterable set of task names
*/
- public function __construct($task_path, $phpEx, phpbb_cache_driver_interface $cache = null)
+ public function __construct($task_names)
{
- if (DIRECTORY_SEPARATOR != '/')
- {
- // Need this on some platforms since the code elsewhere uses /
- // to separate directory components, but PHP iterators return
- // paths with platform-specific directory separators.
- $task_path = str_replace('/', DIRECTORY_SEPARATOR, $task_path);
- }
-
- $this->task_path = $task_path;
- $this->phpEx = $phpEx;
- $this->cache = $cache;
-
- $task_names = $this->find_cron_task_names();
$this->load_tasks($task_names);
}
/**
- * Finds cron task names.
- *
- * A cron task file must follow the naming convention:
- * includes/cron/task/$mod/$name.php.
- * $mod is core for tasks that are part of phpbb.
- * Modifications should use their name as $mod.
- * $name is the name of the cron task.
- * Cron task is expected to be a class named phpbb_cron_task_${mod}_${name}.
- *
- * @return array List of task names
- */
- public function find_cron_task_names()
- {
- if ($this->cache)
- {
- $task_names = $this->cache->get('_cron_tasks');
-
- if ($task_names !== false)
- {
- return $task_names;
- }
- }
-
- $task_names = array();
- $ext = '.' . $this->phpEx;
- $ext_length = strlen($ext);
-
- $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->task_path));
-
- foreach ($iterator as $fileinfo)
- {
- $file = preg_replace('#^' . preg_quote($this->task_path, '#') . '#', '', $fileinfo->getPathname());
-
- // skip directories and files direclty in the task root path
- if ($fileinfo->isFile() && strpos($file, DIRECTORY_SEPARATOR) !== false)
- {
- $task_name = str_replace(DIRECTORY_SEPARATOR, '_', substr($file, 0, -$ext_length));
- if (substr($file, -$ext_length) == $ext && $this->is_valid_name($task_name))
- {
- $task_names[] = 'phpbb_cron_task_' . $task_name;
- }
- }
- }
-
- if ($this->cache)
- {
- $this->cache->put('_cron_tasks', $task_names, 3600);
- }
-
- return $task_names;
- }
-
- /**
- * Checks whether $name is a valid identifier, and
- * therefore part of valid cron task class name.
- *
- * @param string $name Name to check
- *
- * @return bool
- */
- public function is_valid_name($name)
- {
- return (bool) preg_match('/^[a-zA-Z][a-zA-Z0-9_]*$/', $name);
- }
-
- /**
* Loads tasks given by name, wraps them
* and puts them into $this->tasks.
*
- * @param array $task_names Array of strings
+ * @param array|Traversable $task_names Array of strings
*
* @return void
*/
- public function load_tasks(array $task_names)
+ public function load_tasks($task_names)
{
foreach ($task_names as $task_name)
{
diff --git a/phpBB/includes/cron/task/provider.php b/phpBB/includes/cron/task/provider.php
new file mode 100644
index 0000000000..e6ae0f75ec
--- /dev/null
+++ b/phpBB/includes/cron/task/provider.php
@@ -0,0 +1,48 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Provides cron manager with tasks
+*
+* Finds installed cron tasks and makes them available to the cron manager.
+*
+* @package phpBB3
+*/
+class phpbb_cron_task_provider extends phpbb_extension_provider
+{
+ /**
+ * Finds cron task names using the extension manager.
+ *
+ * All PHP files in includes/cron/task/core/ are considered tasks. Tasks
+ * in extensions have to be located in a directory called cron or a subdir
+ * of a directory called cron. The class and filename must end in a _task
+ * suffix. Additionally all PHP files in includes/cron/task/core/ are
+ * tasks.
+ *
+ * @return array List of task names
+ */
+ protected function find()
+ {
+ $finder = $this->extension_manager->get_finder();
+
+ return $finder
+ ->extension_suffix('_task')
+ ->extension_directory('/cron')
+ ->core_path('includes/cron/task/core/')
+ ->get_classes();
+ }
+}
diff --git a/phpBB/includes/extension/base.php b/phpBB/includes/extension/base.php
new file mode 100644
index 0000000000..d9159d57d2
--- /dev/null
+++ b/phpBB/includes/extension/base.php
@@ -0,0 +1,57 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* A base class for extensions without custom enable/disable/purge code.
+*
+* @package extension
+*/
+class phpbb_extension_base implements phpbb_extension_interface
+{
+ /**
+ * Single enable step that does nothing
+ *
+ * @param mixed $old_state State returned by previous call of this method
+ * @return false Indicates no further steps are required
+ */
+ public function enable_step($old_state)
+ {
+ return false;
+ }
+
+ /**
+ * Single disable step that does nothing
+ *
+ * @param mixed $old_state State returned by previous call of this method
+ * @return false Indicates no further steps are required
+ */
+ public function disable_step($old_state)
+ {
+ return false;
+ }
+
+ /**
+ * Single purge step that does nothing
+ *
+ * @param mixed $old_state State returned by previous call of this method
+ * @return false Indicates no further steps are required
+ */
+ public function purge_step($old_state)
+ {
+ return false;
+ }
+}
diff --git a/phpBB/includes/extension/finder.php b/phpBB/includes/extension/finder.php
new file mode 100644
index 0000000000..a1e6b2b347
--- /dev/null
+++ b/phpBB/includes/extension/finder.php
@@ -0,0 +1,417 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* The extension finder provides a simple way to locate files in active extensions
+*
+* @package extension
+*/
+class phpbb_extension_finder
+{
+ protected $extension_manager;
+ protected $phpbb_root_path;
+ protected $cache;
+ protected $phpEx;
+
+ /**
+ * The cache variable name used to store $this->cached_queries in $this->cache.
+ *
+ * Allows the use of multiple differently configured finders with the same cache.
+ * @var string
+ */
+ protected $cache_name;
+
+ /**
+ * An associative array, containing all search parameters set in methods.
+ * @var array
+ */
+ protected $query;
+
+ /**
+ * A map from md5 hashes of serialized queries to their previously retrieved
+ * results.
+ * @var array
+ */
+ protected $cached_queries;
+
+ /**
+ * Creates a new finder instance with its dependencies
+ *
+ * @param phpbb_extension_manager $extension_manager An extension manager
+ * instance that provides the finder with a list of active
+ * extensions and their locations
+ * @param string $phpbb_root_path Path to the phpbb root directory
+ * @param phpbb_cache_driver_interface $cache A cache instance or null
+ * @param string $phpEx php file extension
+ * @param string $cache_name The name of the cache variable, defaults to
+ * _ext_finder
+ */
+ public function __construct(phpbb_extension_manager $extension_manager, $phpbb_root_path = '', phpbb_cache_driver_interface $cache = null, $phpEx = '.php', $cache_name = '_ext_finder')
+ {
+ $this->extension_manager = $extension_manager;
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->cache = $cache;
+ $this->phpEx = $phpEx;
+ $this->cache_name = $cache_name;
+
+ $this->query = array(
+ 'core_path' => false,
+ 'core_suffix' => false,
+ 'core_prefix' => false,
+ 'core_directory' => false,
+ 'extension_suffix' => false,
+ 'extension_prefix' => false,
+ 'extension_directory' => false,
+ );
+
+ $this->cached_queries = ($this->cache) ? $this->cache->get($this->cache_name) : false;
+ }
+
+ /**
+ * Sets a core path to be searched in addition to extensions
+ *
+ * @param string $core_path The path relative to phpbb_root_path
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function core_path($core_path)
+ {
+ $this->query['core_path'] = $core_path;
+ return $this;
+ }
+
+ /**
+ * Sets the suffix all files found in extensions and core must match.
+ *
+ * There is no default file extension, so to find PHP files only, you will
+ * have to specify .php as a suffix. However when using get_classes, the .php
+ * file extension is automatically added to suffixes.
+ *
+ * @param string $suffix A filename suffix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function suffix($suffix)
+ {
+ $this->core_suffix($suffix);
+ $this->extension_suffix($suffix);
+ return $this;
+ }
+
+ /**
+ * Sets a suffix all files found in extensions must match
+ *
+ * There is no default file extension, so to find PHP files only, you will
+ * have to specify .php as a suffix. However when using get_classes, the .php
+ * file extension is automatically added to suffixes.
+ *
+ * @param string $extension_suffix A filename suffix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function extension_suffix($extension_suffix)
+ {
+ $this->query['extension_suffix'] = $extension_suffix;
+ return $this;
+ }
+
+ /**
+ * Sets a suffix all files found in the core path must match
+ *
+ * There is no default file extension, so to find PHP files only, you will
+ * have to specify .php as a suffix. However when using get_classes, the .php
+ * file extension is automatically added to suffixes.
+ *
+ * @param string $core_suffix A filename suffix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function core_suffix($core_suffix)
+ {
+ $this->query['core_suffix'] = $core_suffix;
+ return $this;
+ }
+
+ /**
+ * Sets the prefix all files found in extensions and core must match
+ *
+ * @param string $prefix A filename prefix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function prefix($prefix)
+ {
+ $this->core_prefix($prefix);
+ $this->extension_prefix($prefix);
+ return $this;
+ }
+
+ /**
+ * Sets a prefix all files found in extensions must match
+ *
+ * @param string $extension_prefix A filename prefix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function extension_prefix($extension_prefix)
+ {
+ $this->query['extension_prefix'] = $extension_prefix;
+ return $this;
+ }
+
+ /**
+ * Sets a prefix all files found in the core path must match
+ *
+ * @param string $core_prefix A filename prefix
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function core_prefix($core_prefix)
+ {
+ $this->query['core_prefix'] = $core_prefix;
+ return $this;
+ }
+
+ /**
+ * Sets a directory all files found in extensions and core must be contained in
+ *
+ * Automatically sets the core_directory if its value does not differ from
+ * the current directory.
+ *
+ * @param string $directory
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function directory($directory)
+ {
+ $this->core_directory($directory);
+ $this->extension_directory($directory);
+ return $this;
+ }
+
+ /**
+ * Sets a directory all files found in extensions must be contained in
+ *
+ * @param string $extension_directory
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function extension_directory($extension_directory)
+ {
+ $this->query['extension_directory'] = $this->sanitise_directory($extension_directory);
+ return $this;
+ }
+
+ /**
+ * Sets a directory all files found in the core path must be contained in
+ *
+ * @param string $core_directory
+ * @return phpbb_extension_finder This object for chaining calls
+ */
+ public function core_directory($core_directory)
+ {
+ $this->query['core_directory'] = $this->sanitise_directory($core_directory);
+ return $this;
+ }
+
+ /**
+ * Removes occurances of /./ and makes sure path ends without trailing slash
+ *
+ * @param string $directory A directory pattern
+ * @return string A cleaned up directory pattern
+ */
+ protected function sanitise_directory($directory)
+ {
+ $directory = preg_replace('#(?:^|/)\./#', '/', $directory);
+ $dir_len = strlen($directory);
+
+ if ($dir_len > 1 && $directory[$dir_len - 1] === '/')
+ {
+ $directory = substr($directory, 0, -1);
+ }
+
+ return $directory;
+ }
+
+ /**
+ * Finds classes matching the configured options if they follow phpBB naming rules.
+ *
+ * The php file extension is automatically added to suffixes.
+ *
+ * Note: If a file is matched but contains a class name not following the
+ * phpBB naming rules an incorrect class name will be returned.
+ *
+ * @param bool $cache Whether the result should be cached
+ * @return array An array of found class names
+ */
+ public function get_classes($cache = true)
+ {
+ $this->query['extension_suffix'] .= $this->phpEx;
+ $this->query['core_suffix'] .= $this->phpEx;
+
+ $files = $this->find($cache, false);
+
+ $classes = array();
+ foreach ($files as $file => $ext_name)
+ {
+ $file = preg_replace('#^includes/#', '', $file);
+
+ $classes[] = 'phpbb_' . str_replace('/', '_', substr($file, 0, -strlen($this->phpEx)));
+ }
+ return $classes;
+ }
+
+ /**
+ * Finds all directories matching the configured options
+ *
+ * @param bool $cache Whether the result should be cached
+ * @return array An array of paths to found directories
+ */
+ public function get_directories($cache = true)
+ {
+ return $this->find_with_root_path($cache, true);
+ }
+
+ /**
+ * Finds all files matching the configured options.
+ *
+ * @param bool $cache Whether the result should be cached
+ * @return array An array of paths to found files
+ */
+ public function get_files($cache = true)
+ {
+ return $this->find_with_root_path($cache, false);
+ }
+
+ /**
+ * A wrapper around the general find which prepends a root path to results
+ *
+ * @param bool $cache Whether the result should be cached
+ * @param bool $is_dir Directories will be returned when true, only files
+ * otherwise
+ * @return array An array of paths to found items
+ */
+ protected function find_with_root_path($cache = true, $is_dir = false)
+ {
+ $items = $this->find($cache, $is_dir);
+
+ $result = array();
+ foreach ($items as $item => $ext_name)
+ {
+ $result[] = $this->phpbb_root_path . $item;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Finds all file system entries matching the configured options
+ *
+ * @param bool $cache Whether the result should be cached
+ * @param bool $is_dir Directories will be returned when true, only files
+ * otherwise
+ * @return array An array of paths to found items
+ */
+ public function find($cache = true, $is_dir = false)
+ {
+ $this->query['is_dir'] = $is_dir;
+ $query = md5(serialize($this->query));
+
+ if (!defined('DEBUG') && $cache && isset($this->cached_queries[$query]))
+ {
+ return $this->cached_queries[$query];
+ }
+
+ $files = array();
+
+ $extensions = $this->extension_manager->all_enabled();
+
+ if ($this->query['core_path'])
+ {
+ $extensions['/'] = $this->phpbb_root_path . $this->query['core_path'];
+ }
+
+ foreach ($extensions as $name => $path)
+ {
+ $ext_name = $name;
+
+ if (!file_exists($path))
+ {
+ continue;
+ }
+
+ if ($name === '/')
+ {
+ $location = $this->query['core_path'];
+ $name = '';
+ $suffix = $this->query['core_suffix'];
+ $prefix = $this->query['core_prefix'];
+ $directory = $this->query['core_directory'];
+ }
+ else
+ {
+ $location = 'ext/';
+ $name .= '/';
+ $suffix = $this->query['extension_suffix'];
+ $prefix = $this->query['extension_prefix'];
+ $directory = $this->query['extension_directory'];
+ }
+
+ // match only first directory if leading slash is given
+ if ($directory === '/')
+ {
+ $directory_pattern = '^' . preg_quote(DIRECTORY_SEPARATOR, '#');
+ }
+ else if ($directory && $directory[0] === '/')
+ {
+ $directory_pattern = '^' . preg_quote(str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR, '#');
+ }
+ else
+ {
+ $directory_pattern = preg_quote(DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $directory) . DIRECTORY_SEPARATOR, '#');
+ }
+ $directory_pattern = '#' . $directory_pattern . '#';
+
+ $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST);
+ foreach ($iterator as $file_info)
+ {
+ if ($file_info->isDir() == $is_dir)
+ {
+ if ($is_dir)
+ {
+ $relative_path = $iterator->getInnerIterator()->getSubPath() . DIRECTORY_SEPARATOR . basename($file_info->getFilename()) . DIRECTORY_SEPARATOR;
+ if ($relative_path[0] !== DIRECTORY_SEPARATOR)
+ {
+ $relative_path = DIRECTORY_SEPARATOR . $relative_path;
+ }
+ }
+ else
+ {
+ $relative_path = DIRECTORY_SEPARATOR . $iterator->getInnerIterator()->getSubPathname();
+ }
+ $item_name = $file_info->getFilename();
+
+ if ((!$suffix || substr($relative_path, -strlen($suffix)) === $suffix) &&
+ (!$prefix || substr($item_name, 0, strlen($prefix)) === $prefix) &&
+ (!$directory || preg_match($directory_pattern, $relative_path)))
+ {
+ $files[str_replace(DIRECTORY_SEPARATOR, '/', $location . $name . substr($relative_path, 1))] = $ext_name;
+ }
+ }
+ }
+ }
+
+ if ($cache && $this->cache)
+ {
+ $this->cached_queries[$query] = $files;
+ $this->cache->put($this->cache_name, $this->cached_queries);
+ }
+
+ return $files;
+ }
+}
diff --git a/phpBB/includes/extension/interface.php b/phpBB/includes/extension/interface.php
new file mode 100644
index 0000000000..b37cd24d77
--- /dev/null
+++ b/phpBB/includes/extension/interface.php
@@ -0,0 +1,65 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* The interface extension meta classes have to implement to run custom code
+* on enable/disable/purge.
+*
+* @package extension
+*/
+interface phpbb_extension_interface
+{
+ /**
+ * enable_step is executed on enabling an extension until it returns false.
+ *
+ * Calls to this function can be made in subsequent requests, when the
+ * function is invoked through a webserver with a too low max_execution_time.
+ *
+ * @param mixed $old_state The return value of the previous call
+ * of this method, or false on the first call
+ * @return mixed Returns false after last step, otherwise
+ * temporary state which is passed as an
+ * argument to the next step
+ */
+ public function enable_step($old_state);
+
+ /**
+ * Disables the extension.
+ *
+ * Calls to this function can be made in subsequent requests, when the
+ * function is invoked through a webserver with a too low max_execution_time.
+ *
+ * @param mixed $old_state The return value of the previous call
+ * of this method, or false on the first call
+ * @return null
+ */
+ public function disable_step($old_state);
+
+ /**
+ * purge_step is executed on purging an extension until it returns false.
+ *
+ * Calls to this function can be made in subsequent requests, when the
+ * function is invoked through a webserver with a too low max_execution_time.
+ *
+ * @param mixed $old_state The return value of the previous call
+ * of this method, or false on the first call
+ * @return mixed Returns false after last step, otherwise
+ * temporary state which is passed as an
+ * argument to the next step
+ */
+ public function purge_step($old_state);
+}
diff --git a/phpBB/includes/extension/manager.php b/phpBB/includes/extension/manager.php
new file mode 100644
index 0000000000..bcdd21f7f1
--- /dev/null
+++ b/phpBB/includes/extension/manager.php
@@ -0,0 +1,415 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* The extension manager provides means to activate/deactivate extensions.
+*
+* @package extension
+*/
+class phpbb_extension_manager
+{
+ protected $cache;
+ protected $phpEx;
+ protected $extensions;
+ protected $extension_table;
+ protected $phpbb_root_path;
+ protected $cache_name;
+
+ /**
+ * Creates a manager and loads information from database
+ *
+ * @param dbal $db A database connection
+ * @param string $extension_table The name of the table holding extensions
+ * @param string $phpbb_root_path Path to the phpbb includes directory.
+ * @param string $phpEx php file extension
+ * @param phpbb_cache_driver_interface $cache A cache instance or null
+ * @param string $cache_name The name of the cache variable, defaults to _ext
+ */
+ public function __construct(dbal $db, $extension_table, $phpbb_root_path, $phpEx = '.php', phpbb_cache_driver_interface $cache = null, $cache_name = '_ext')
+ {
+ $this->phpbb_root_path = $phpbb_root_path;
+ $this->db = $db;
+ $this->cache = $cache;
+ $this->phpEx = $phpEx;
+ $this->extension_table = $extension_table;
+ $this->cache_name = $cache_name;
+
+ $this->extensions = ($this->cache) ? $this->cache->get($this->cache_name) : false;
+
+ if ($this->extensions === false)
+ {
+ $this->load_extensions();
+ }
+ }
+
+ /**
+ * Loads all extension information from the database
+ *
+ * @return null
+ */
+ protected function load_extensions()
+ {
+ $sql = 'SELECT *
+ FROM ' . $this->extension_table;
+
+ $result = $this->db->sql_query($sql);
+ $extensions = $this->db->sql_fetchrowset($result);
+ $this->db->sql_freeresult($result);
+
+ $this->extensions = array();
+ foreach ($extensions as $extension)
+ {
+ $extension['ext_path'] = $this->get_extension_path($extension['ext_name']);
+ $this->extensions[$extension['ext_name']] = $extension;
+ }
+
+ ksort($this->extensions);
+
+ if ($this->cache)
+ {
+ $this->cache->put($this->cache_name, $this->extensions);
+ }
+ }
+
+ /**
+ * Generates the path to an extension
+ *
+ * @param string $name The name of the extension
+ * @param bool $phpbb_relative Whether the path should be relative to phpbb root
+ * @return string Path to an extension
+ */
+ public function get_extension_path($name, $phpbb_relative = false)
+ {
+ $name = str_replace('.', '', $name);
+
+ return (($phpbb_relative) ? $this->phpbb_root_path : '') . 'ext/' . $name . '/';
+ }
+
+ /**
+ * Instantiates the extension meta class for the extension with the given name
+ *
+ * @param string $name The extension name
+ * @return phpbb_extension_interface Instance of the extension meta class or
+ * phpbb_extension_base if the class does not exist
+ */
+ public function get_extension($name)
+ {
+ $extension_class_name = 'phpbb_ext_' . str_replace('/', '_', $name) . '_ext';
+
+ if (class_exists($extension_class_name))
+ {
+ return new $extension_class_name;
+ }
+ else
+ {
+ return new phpbb_extension_base;
+ }
+ }
+
+ /**
+ * Runs a step of the extension enabling process.
+ *
+ * Allows the exentension to enable in a long running script that works
+ * in multiple steps across requests. State is kept for the extension
+ * in the extensions table.
+ *
+ * @param string $name The extension's name
+ * @return bool False if enabling is finished, true otherwise
+ */
+ public function enable_step($name)
+ {
+ // ignore extensions that are already enabled
+ if (isset($this->extensions[$name]) && $this->extensions[$name]['ext_active'])
+ {
+ return false;
+ }
+
+ $old_state = (isset($this->extensions[$name]['ext_state'])) ? unserialize($this->extensions[$name]['ext_state']) : false;
+
+ $extension = $this->get_extension($name);
+ $state = $extension->enable_step($old_state);
+
+ $active = ($state === false);
+
+ $extension_data = array(
+ 'ext_name' => $name,
+ 'ext_active' => $active,
+ 'ext_state' => serialize($state),
+ );
+
+ $this->extensions[$name] = $extension_data;
+ $this->extensions[$name]['ext_path'] = $this->get_extension_path($extension_data['ext_name']);
+ ksort($this->extensions);
+
+ $sql = 'UPDATE ' . $this->extension_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . "
+ WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ if (!$this->db->sql_affectedrows())
+ {
+ $sql = 'INSERT INTO ' . $this->extension_table . '
+ ' . $this->db->sql_build_array('INSERT', $extension_data);
+ $this->db->sql_query($sql);
+ }
+
+ return !$active;
+ }
+
+ /**
+ * Enables an extension
+ *
+ * This method completely enables an extension. But it could be long running
+ * so never call this in a script that has a max_execution time.
+ *
+ * @param string $name The extension's name
+ * @return null
+ */
+ public function enable($name)
+ {
+ while ($this->enable_step($name));
+ }
+
+ /**
+ * Disables an extension
+ *
+ * Calls the disable method on the extension's meta class to allow it to
+ * process the event.
+ *
+ * @param string $name The extension's name
+ * @return bool False if disabling is finished, true otherwise
+ */
+ public function disable_step($name)
+ {
+ // ignore extensions that are already disabled
+ if (!isset($this->extensions[$name]) || !$this->extensions[$name]['ext_active'])
+ {
+ return false;
+ }
+
+ $old_state = unserialize($this->extensions[$name]['ext_state']);
+
+ $extension = $this->get_extension($name);
+ $state = $extension->disable_step($old_state);
+
+ // continue until the state is false
+ if ($state !== false)
+ {
+ $extension_data = array(
+ 'ext_state' => serialize($state),
+ );
+ $this->extensions[$name]['ext_state'] = serialize($state);
+
+ $sql = 'UPDATE ' . $this->extension_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . "
+ WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ return true;
+ }
+
+ $extension_data = array(
+ 'ext_active' => false,
+ 'ext_state' => serialize(false),
+ );
+ $this->extensions[$name]['ext_active'] = false;
+ $this->extensions[$name]['ext_state'] = serialize(false);
+
+ $sql = 'UPDATE ' . $this->extension_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . "
+ WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ return false;
+ }
+
+ /**
+ * Disables an extension
+ *
+ * Disables an extension completely at once. This process could run for a
+ * while so never call this in a script that has a max_execution time.
+ *
+ * @param string $name The extension's name
+ * @return null
+ */
+ public function disable($name)
+ {
+ while ($this->disable_step($name));
+ }
+
+ /**
+ * Purge an extension
+ *
+ * Disables the extension first if active, and then calls purge on the
+ * extension's meta class to delete the extension's database content.
+ *
+ * @param string $name The extension's name
+ * @return bool False if purging is finished, true otherwise
+ */
+ public function purge_step($name)
+ {
+ // ignore extensions that do not exist
+ if (!isset($this->extensions[$name]))
+ {
+ return false;
+ }
+
+ // disable first if necessary
+ if ($this->extensions[$name]['ext_active'])
+ {
+ $this->disable($name);
+ }
+
+ $old_state = unserialize($this->extensions[$name]['ext_state']);
+
+ $extension = $this->get_extension($name);
+ $state = $extension->purge_step($old_state);
+
+ // continue until the state is false
+ if ($state !== false)
+ {
+ $extension_data = array(
+ 'ext_state' => serialize($state),
+ );
+ $this->extensions[$name]['ext_state'] = serialize($state);
+
+ $sql = 'UPDATE ' . $this->extension_table . '
+ SET ' . $this->db->sql_build_array('UPDATE', $extension_data) . "
+ WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ return true;
+ }
+
+ unset($this->extensions[$name]);
+
+ $sql = 'DELETE FROM ' . $this->extension_table . "
+ WHERE ext_name = '" . $this->db->sql_escape($name) . "'";
+ $this->db->sql_query($sql);
+
+ return false;
+ }
+
+ /**
+ * Purge an extension
+ *
+ * Purges an extension completely at once. This process could run for a while
+ * so never call this in a script that has a max_execution time.
+ *
+ * @param string $name The extension's name
+ * @return null
+ */
+ public function purge($name)
+ {
+ while ($this->purge_step($name));
+ }
+
+ /**
+ * Retrieves a list of all available extensions on the filesystem
+ *
+ * @return array An array with extension names as keys and paths to the
+ * extension as values
+ */
+ public function all_available()
+ {
+ $available = array();
+
+ $iterator = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($this->phpbb_root_path . 'ext/'));
+ foreach ($iterator as $file_info)
+ {
+ if ($file_info->isFile() && $file_info->getFilename() == 'ext' . $this->phpEx)
+ {
+ $ext_name = $iterator->getInnerIterator()->getSubPath();
+
+ $ext_name = str_replace(DIRECTORY_SEPARATOR, '/', $ext_name);
+
+ $available[$ext_name] = $this->phpbb_root_path . 'ext/' . $ext_name . '/';
+ }
+ }
+ ksort($available);
+ return $available;
+ }
+
+ /**
+ * Retrieves all configured extensions.
+ *
+ * All enabled and disabled extensions are considered configured. A purged
+ * extension that is no longer in the database is not configured.
+ *
+ * @return array An array with extension names as keys and and the
+ * database stored extension information as values
+ */
+ public function all_configured()
+ {
+ $configured = array();
+ foreach ($this->extensions as $name => $data)
+ {
+ $data['ext_path'] = $this->phpbb_root_path . $data['ext_path'];
+ $configured[$name] = $data;
+ }
+ return $configured;
+ }
+
+ /**
+ * Retrieves all enabled extensions.
+ *
+ * @return array An array with extension names as keys and and the
+ * database stored extension information as values
+ */
+ public function all_enabled()
+ {
+ $enabled = array();
+ foreach ($this->extensions as $name => $data)
+ {
+ if ($data['ext_active'])
+ {
+ $enabled[$name] = $this->phpbb_root_path . $data['ext_path'];
+ }
+ }
+ return $enabled;
+ }
+
+ /**
+ * Retrieves all disabled extensions.
+ *
+ * @return array An array with extension names as keys and and the
+ * database stored extension information as values
+ */
+ public function all_disabled()
+ {
+ $disabled = array();
+ foreach ($this->extensions as $name => $data)
+ {
+ if (!$data['ext_active'])
+ {
+ $disabled[$name] = $this->phpbb_root_path . $data['ext_path'];
+ }
+ }
+ return $disabled;
+ }
+
+ /**
+ * Instantiates a phpbb_extension_finder.
+ *
+ * @return phpbb_extension_finder An extension finder instance
+ */
+ public function get_finder()
+ {
+ return new phpbb_extension_finder($this, $this->phpbb_root_path, $this->cache, $this->phpEx, $this->cache_name . '_finder');
+ }
+}
diff --git a/phpBB/includes/extension/provider.php b/phpBB/includes/extension/provider.php
new file mode 100644
index 0000000000..3939c2ef07
--- /dev/null
+++ b/phpBB/includes/extension/provider.php
@@ -0,0 +1,68 @@
+<?php
+/**
+*
+* @package extension
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Provides a set of items found in extensions
+*
+* @package extension
+*/
+abstract class phpbb_extension_provider implements IteratorAggregate
+{
+ /**
+ * Array holding all found items
+ * @var array|null
+ */
+ protected $items = null;
+
+ /**
+ * An extension manager to search for items in extensions
+ * @var phpbb_extension_manager
+ */
+ protected $extension_manager;
+
+ /**
+ * Constructor. Loads all available items.
+ *
+ * @param phpbb_extension_manager $extension_manager phpBB extension manager
+ */
+ public function __construct(phpbb_extension_manager $extension_manager)
+ {
+ $this->extension_manager = $extension_manager;
+ }
+
+ /**
+ * Finds template paths using the extension manager.
+ *
+ * @return array List of task names
+ */
+ abstract protected function find();
+
+ /**
+ * Retrieve an iterator over all items
+ *
+ * @return ArrayIterator An iterator for the array of template paths
+ */
+ public function getIterator()
+ {
+ if ($this->items === null)
+ {
+ $this->items = $this->find();
+ }
+
+ return new ArrayIterator($this->items);
+ }
+}
diff --git a/phpBB/includes/functions_messenger.php b/phpBB/includes/functions_messenger.php
index b94957a85f..1149d03347 100644
--- a/phpBB/includes/functions_messenger.php
+++ b/phpBB/includes/functions_messenger.php
@@ -175,7 +175,7 @@ class messenger
*/
function template($template_file, $template_lang = '', $template_path = '')
{
- global $config, $phpbb_root_path, $phpEx, $user;
+ global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
if (!trim($template_file))
{
@@ -194,7 +194,8 @@ class messenger
if (!isset($this->tpl_msg[$template_lang . $template_file]))
{
$template_locator = new phpbb_template_locator();
- $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator);
+ $template_path_provider = new phpbb_template_extension_path_provider($phpbb_extension_manager, new phpbb_template_path_provider());
+ $this->tpl_msg[$template_lang . $template_file] = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator, $template_path_provider);
$tpl = &$this->tpl_msg[$template_lang . $template_file];
$fallback_template_path = false;
diff --git a/phpBB/includes/functions_module.php b/phpBB/includes/functions_module.php
index 09c54422b0..1f451d392d 100644
--- a/phpBB/includes/functions_module.php
+++ b/phpBB/includes/functions_module.php
@@ -221,13 +221,15 @@ class p_master
// We need to prefix the functions to not create a naming conflict
// Function for building 'url_extra'
- $url_func = '_module_' . $row['module_basename'] . '_url';
+ $short_name = $this->get_short_name($row['module_basename']);
+
+ $url_func = '_module_' . $short_name . '_url';
// Function for building the language name
- $lang_func = '_module_' . $row['module_basename'] . '_lang';
+ $lang_func = '_module_' . $short_name . '_lang';
// Custom function for calling parameters on module init (for example assigning template variables)
- $custom_func = '_module_' . $row['module_basename'];
+ $custom_func = '_module_' . $short_name;
$names[$row['module_basename'] . '_' . $row['module_mode']][] = true;
@@ -275,6 +277,11 @@ class p_master
*/
function loaded($module_basename, $module_mode = false)
{
+ if (!$this->is_full_class($module_basename))
+ {
+ $module_basename = $this->p_class . '_' . $module_basename;
+ }
+
if (empty($this->loaded_cache))
{
$this->loaded_cache = array();
@@ -381,6 +388,11 @@ class p_master
$id = request_var('icat', '');
}
+ if ($id && !is_numeric($id) && !$this->is_full_class($id))
+ {
+ $id = $this->p_class . '_' . $id;
+ }
+
$category = false;
foreach ($this->module_ary as $row_id => $item_ary)
{
@@ -389,9 +401,9 @@ class p_master
// If this is a module and no mode selected, select first mode
// If no category or module selected, go active for first module in first category
if (
- (($item_ary['name'] === $id || $item_ary['id'] === (int) $id) && (($item_ary['mode'] == $mode && !$item_ary['cat']) || ($icat && $item_ary['cat']))) ||
+ (($item_ary['name'] === $id || $item_ary['name'] === $this->p_class . '_' . $id || $item_ary['id'] === (int) $id) && (($item_ary['mode'] == $mode && !$item_ary['cat']) || ($icat && $item_ary['cat']))) ||
($item_ary['parent'] === $category && !$item_ary['cat'] && !$icat && $item_ary['display']) ||
- (($item_ary['name'] === $id || $item_ary['id'] === (int) $id) && !$mode && !$item_ary['cat']) ||
+ (($item_ary['name'] === $id || $item_ary['name'] === $this->p_class . '_' . $id || $item_ary['id'] === (int) $id) && !$mode && !$item_ary['cat']) ||
(!$id && !$mode && !$item_ary['cat'] && $item_ary['display'])
)
{
@@ -440,75 +452,74 @@ class p_master
trigger_error('Module not accessible', E_USER_ERROR);
}
- if (!class_exists("{$this->p_class}_$this->p_name"))
+ // new modules use the full class names, old ones are always called <type>_<name>, e.g. acp_board
+ if (!class_exists($this->p_name))
{
- if (!file_exists("$module_path/{$this->p_class}_$this->p_name.$phpEx"))
+ if (!file_exists("$module_path/{$this->p_name}.$phpEx"))
{
- trigger_error("Cannot find module $module_path/{$this->p_class}_$this->p_name.$phpEx", E_USER_ERROR);
+ trigger_error("Cannot find module $module_path/{$this->p_name}.$phpEx", E_USER_ERROR);
}
- include("$module_path/{$this->p_class}_$this->p_name.$phpEx");
+ include("$module_path/{$this->p_name}.$phpEx");
- if (!class_exists("{$this->p_class}_$this->p_name"))
+ if (!class_exists($this->p_name))
{
- trigger_error("Module file $module_path/{$this->p_class}_$this->p_name.$phpEx does not contain correct class [{$this->p_class}_$this->p_name]", E_USER_ERROR);
+ trigger_error("Module file $module_path/{$this->p_name}.$phpEx does not contain correct class [{$this->p_name}]", E_USER_ERROR);
}
+ }
- if (!empty($mode))
- {
- $this->p_mode = $mode;
- }
+ if (!empty($mode))
+ {
+ $this->p_mode = $mode;
+ }
- // Create a new instance of the desired module ... if it has a
- // constructor it will of course be executed
- $instance = "{$this->p_class}_$this->p_name";
+ // Create a new instance of the desired module ...
+ $class_name = $this->p_name;
- $this->module = new $instance($this);
+ $this->module = new $class_name($this);
- // We pre-define the action parameter we are using all over the place
- if (defined('IN_ADMIN'))
+ // We pre-define the action parameter we are using all over the place
+ if (defined('IN_ADMIN'))
+ {
+ // Is first module automatically enabled a duplicate and the category not passed yet?
+ if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate'])
{
- // Is first module automatically enabled a duplicate and the category not passed yet?
- if (!$icat && $this->module_ary[$this->active_module_row_id]['is_duplicate'])
- {
- $icat = $this->module_ary[$this->active_module_row_id]['parent'];
- }
+ $icat = $this->module_ary[$this->active_module_row_id]['parent'];
+ }
- // Not being able to overwrite ;)
- $this->module->u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i={$this->p_name}") . (($icat) ? '&amp;icat=' . $icat : '') . "&amp;mode={$this->p_mode}";
+ // Not being able to overwrite ;)
+ $this->module->u_action = append_sid("{$phpbb_admin_path}index.$phpEx", "i={$this->p_name}") . (($icat) ? '&amp;icat=' . $icat : '') . "&amp;mode={$this->p_mode}";
+ }
+ else
+ {
+ // If user specified the module url we will use it...
+ if ($module_url !== false)
+ {
+ $this->module->u_action = $module_url;
}
else
{
- // If user specified the module url we will use it...
- if ($module_url !== false)
- {
- $this->module->u_action = $module_url;
- }
- else
- {
- $this->module->u_action = $phpbb_root_path . (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name'];
- }
-
- $this->module->u_action = append_sid($this->module->u_action, "i={$this->p_name}") . (($icat) ? '&amp;icat=' . $icat : '') . "&amp;mode={$this->p_mode}";
+ $this->module->u_action = $phpbb_root_path . (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name'];
}
- // Add url_extra parameter to u_action url
- if (!empty($this->module_ary) && $this->active_module !== false && $this->module_ary[$this->active_module_row_id]['url_extra'])
- {
- $this->module->u_action .= $this->module_ary[$this->active_module_row_id]['url_extra'];
- }
+ $this->module->u_action = append_sid($this->module->u_action, "i={$this->p_name}") . (($icat) ? '&amp;icat=' . $icat : '') . "&amp;mode={$this->p_mode}";
+ }
- // Assign the module path for re-usage
- $this->module->module_path = $module_path . '/';
+ // Add url_extra parameter to u_action url
+ if (!empty($this->module_ary) && $this->active_module !== false && $this->module_ary[$this->active_module_row_id]['url_extra'])
+ {
+ $this->module->u_action .= $this->module_ary[$this->active_module_row_id]['url_extra'];
+ }
- // Execute the main method for the new instance, we send the module id and mode as parameters
- // Users are able to call the main method after this function to be able to assign additional parameters manually
- if ($execute_module)
- {
- $this->module->main($this->p_name, $this->p_mode);
- }
+ // Assign the module path for re-usage
+ $this->module->module_path = $module_path . '/';
- return;
+ // Execute the main method for the new instance, we send the module id and mode as parameters
+ // Users are able to call the main method after this function to be able to assign additional parameters manually
+ if ($execute_module)
+ {
+ $short_name = preg_replace("#^{$this->p_class}_#", '', $this->p_name);
+ $this->module->main($short_name, $this->p_mode);
}
}
@@ -547,7 +558,7 @@ class p_master
// If we find a name by this id and being enabled we have our active one...
foreach ($this->module_ary as $row_id => $item_ary)
{
- if (($item_ary['name'] === $id || $item_ary['id'] === (int) $id) && $item_ary['display'])
+ if (($item_ary['name'] === $id || $item_ary['id'] === (int) $id) && $item_ary['display'] || $item_ary['name'] === $this->p_class . '_' . $id)
{
if ($mode === false || $mode === $item_ary['mode'])
{
@@ -841,7 +852,7 @@ class p_master
{
foreach ($this->module_ary as $row_id => $item_ary)
{
- if (($item_ary['name'] === $id || $item_ary['id'] === (int) $id) && (!$mode || $item_ary['mode'] === $mode))
+ if (($item_ary['name'] === $id || $item_ary['name'] === $this->p_class . '_' . $id || $item_ary['id'] === (int) $id) && (!$mode || $item_ary['mode'] === $mode))
{
$this->module_ary[$row_id]['display'] = (int) $display;
}
@@ -855,28 +866,49 @@ class p_master
{
global $user, $phpEx;
- if (file_exists($user->lang_path . $user->lang_name . '/mods'))
- {
- $add_files = array();
+ global $phpbb_extension_manager;
- $dir = @opendir($user->lang_path . $user->lang_name . '/mods');
+ $finder = $phpbb_extension_manager->get_finder();
- if ($dir)
- {
- while (($entry = readdir($dir)) !== false)
- {
- if (strpos($entry, 'info_' . strtolower($module_class) . '_') === 0 && substr(strrchr($entry, '.'), 1) == $phpEx)
- {
- $add_files[] = 'mods/' . substr(basename($entry), 0, -(strlen($phpEx) + 1));
- }
- }
- closedir($dir);
- }
+ $lang_files = $finder
+ ->prefix('info_' . strtolower($module_class) . '_')
+ ->suffix(".$phpEx")
+ ->extension_directory('/language/' . $user->lang_name)
+ ->core_path('language/' . $user->lang_name . '/mods/')
+ ->find();
- if (sizeof($add_files))
- {
- $user->add_lang($add_files);
- }
+ foreach ($lang_files as $lang_file => $ext_name)
+ {
+ $user->add_lang_ext($ext_name, $lang_file);
}
}
+
+ /**
+ * Retrieve shortened module basename for legacy basenames (with xcp_ prefix)
+ *
+ * @param string $basename A module basename
+ * @return string The basename if it starts with phpbb_ or the basename with
+ * the current p_class (e.g. acp_) stripped.
+ */
+ protected function get_short_name($basename)
+ {
+ if (substr($basename, 0, 6) === 'phpbb_')
+ {
+ return $basename;
+ }
+
+ // strip xcp_ prefix from old classes
+ return substr($basename, strlen($this->p_class) + 1);
+ }
+
+ /**
+ * Checks whether the given module basename is a correct class name
+ *
+ * @param string $basename A module basename
+ * @return bool True if the basename starts with phpbb_ or (x)cp_, false otherwise
+ */
+ protected function is_full_class($basename)
+ {
+ return (substr($basename, 0, 6) === 'phpbb_' || substr($basename, 0, strlen($this->p_class) + 1) === $this->p_class . '_');
+ }
}
diff --git a/phpBB/includes/functions_posting.php b/phpBB/includes/functions_posting.php
index 8cd0e4add1..dd8437da1a 100644
--- a/phpBB/includes/functions_posting.php
+++ b/phpBB/includes/functions_posting.php
@@ -2350,16 +2350,11 @@ function submit_post($mode, $subject, $username, $topic_type, &$poll, &$data, $u
if ($update_search_index && $data['enable_indexing'])
{
// Select the search method and do some additional checks to ensure it can actually be utilised
- $search_type = basename($config['search_type']);
-
- if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx))
- {
- trigger_error('NO_SUCH_SEARCH_MODULE');
- }
+ $search_type = $config['search_type'];
if (!class_exists($search_type))
{
- include("{$phpbb_root_path}includes/search/$search_type.$phpEx");
+ trigger_error('NO_SUCH_SEARCH_MODULE');
}
$error = false;
diff --git a/phpBB/includes/search/search.php b/phpBB/includes/search/base.php
index 7c34ce9ff6..bb63957aba 100644
--- a/phpBB/includes/search/search.php
+++ b/phpBB/includes/search/base.php
@@ -24,12 +24,12 @@ define('SEARCH_RESULT_IN_CACHE', 1);
define('SEARCH_RESULT_INCOMPLETE', 2);
/**
-* search_backend
+* phpbb_search_base
* optional base class for search plugins providing simple caching based on ACM
* and functions to retrieve ignore_words and synonyms
* @package search
*/
-class search_backend
+class phpbb_search_base
{
var $ignore_words = array();
var $match_synonym = array();
diff --git a/phpBB/includes/search/fulltext_mysql.php b/phpBB/includes/search/fulltext_mysql.php
index 827205f20d..4214de3d97 100644
--- a/phpBB/includes/search/fulltext_mysql.php
+++ b/phpBB/includes/search/fulltext_mysql.php
@@ -17,16 +17,11 @@ if (!defined('IN_PHPBB'))
}
/**
-* @ignore
-*/
-include_once($phpbb_root_path . 'includes/search/search.' . $phpEx);
-
-/**
* fulltext_mysql
* Fulltext search for MySQL
* @package search
*/
-class fulltext_mysql extends search_backend
+class phpbb_search_fulltext_mysql extends phpbb_search_base
{
var $stats = array();
var $word_length = array();
@@ -36,7 +31,7 @@ class fulltext_mysql extends search_backend
var $pcre_properties = false;
var $mbstring_regex = false;
- function fulltext_mysql(&$error)
+ public function __construct(&$error)
{
global $config;
@@ -58,6 +53,16 @@ class fulltext_mysql extends search_backend
}
/**
+ * Returns the name of this search backend to be displayed to administrators
+ *
+ * @return string Name
+ */
+ public function get_name()
+ {
+ return 'MySQL Fulltext';
+ }
+
+ /**
* Checks for correct MySQL version and stores min/max word length in the config
*/
function init()
diff --git a/phpBB/includes/search/fulltext_native.php b/phpBB/includes/search/fulltext_native.php
index e749e86f68..a335ddab6e 100644
--- a/phpBB/includes/search/fulltext_native.php
+++ b/phpBB/includes/search/fulltext_native.php
@@ -17,16 +17,11 @@ if (!defined('IN_PHPBB'))
}
/**
-* @ignore
-*/
-include_once($phpbb_root_path . 'includes/search/search.' . $phpEx);
-
-/**
* fulltext_native
* phpBB's own db driven fulltext search, version 2
* @package search
*/
-class fulltext_native extends search_backend
+class phpbb_search_fulltext_native extends phpbb_search_base
{
var $stats = array();
var $word_length = array();
@@ -41,10 +36,8 @@ class fulltext_native extends search_backend
* Initialises the fulltext_native search backend with min/max word length and makes sure the UTF-8 normalizer is loaded.
*
* @param boolean|string &$error is passed by reference and should either be set to false on success or an error message on failure.
- *
- * @access public
*/
- function fulltext_native(&$error)
+ public function __construct(&$error)
{
global $phpbb_root_path, $phpEx, $config;
@@ -58,11 +51,20 @@ class fulltext_native extends search_backend
include($phpbb_root_path . 'includes/utf/utf_normalizer.' . $phpEx);
}
-
$error = false;
}
/**
+ * Returns the name of this search backend to be displayed to administrators
+ *
+ * @return string Name
+ */
+ public function get_name()
+ {
+ return 'phpBB Native Fulltext';
+ }
+
+ /**
* This function fills $this->search_query with the cleaned user search query.
*
* If $terms is 'any' then the words will be extracted from the search query
diff --git a/phpBB/includes/session.php b/phpBB/includes/session.php
index bd2257c139..497aaf1141 100644
--- a/phpBB/includes/session.php
+++ b/phpBB/includes/session.php
@@ -1901,6 +1901,7 @@ class user extends session
* @param mixed $lang_set specifies the language entries to include
* @param bool $use_db internal variable for recursion, do not use
* @param bool $use_help internal variable for recursion, do not use
+ * @param string $ext_name The extension to load language from, or empty for core files
*
* Examples:
* <code>
@@ -1911,7 +1912,7 @@ class user extends session
* $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
* </code>
*/
- function add_lang($lang_set, $use_db = false, $use_help = false)
+ function add_lang($lang_set, $use_db = false, $use_help = false, $ext_name = '')
{
global $phpEx;
@@ -1925,36 +1926,54 @@ class user extends session
if ($key == 'db')
{
- $this->add_lang($lang_file, true, $use_help);
+ $this->add_lang($lang_file, true, $use_help, $ext_name);
}
else if ($key == 'help')
{
- $this->add_lang($lang_file, $use_db, true);
+ $this->add_lang($lang_file, $use_db, true, $ext_name);
}
else if (!is_array($lang_file))
{
- $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
+ $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help, $ext_name);
}
else
{
- $this->add_lang($lang_file, $use_db, $use_help);
+ $this->add_lang($lang_file, $use_db, $use_help, $ext_name);
}
}
unset($lang_set);
}
else if ($lang_set)
{
- $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
+ $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help, $ext_name);
}
}
/**
+ * Add Language Items from an extension - use_db and use_help are assigned where needed (only use them to force inclusion)
+ *
+ * @param string $ext_name The extension to load language from, or empty for core files
+ * @param mixed $lang_set specifies the language entries to include
+ * @param bool $use_db internal variable for recursion, do not use
+ * @param bool $use_help internal variable for recursion, do not use
+ */
+ function add_lang_ext($ext_name, $lang_set, $use_db = false, $use_help = false)
+ {
+ if ($ext_name === '/')
+ {
+ $ext_name = '';
+ }
+
+ $this->add_lang($lang_set, $use_db, $use_help, $ext_name);
+ }
+
+ /**
* Set language entry (called by add_lang)
* @access private
*/
- function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
+ function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false, $ext_name = '')
{
- global $phpEx;
+ global $phpbb_root_path, $phpEx;
// Make sure the language name is set (if the user setup did not happen it is not set)
if (!$this->lang_name)
@@ -1970,11 +1989,32 @@ class user extends session
{
if ($use_help && strpos($lang_file, '/') !== false)
{
- $language_filename = $this->lang_path . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . $phpEx;
+ $filename = dirname($lang_file) . '/help_' . basename($lang_file);
+ }
+ else
+ {
+ $filename = (($use_help) ? 'help_' : '') . $lang_file;
+ }
+
+ if ($ext_name)
+ {
+ global $phpbb_extension_manager;
+ $ext_path = $phpbb_extension_manager->get_extension_path($ext_name, true);
+
+ $lang_path = $ext_path . 'language/';
+ }
+ else
+ {
+ $lang_path = $this->lang_path;
+ }
+
+ if (strpos($phpbb_root_path . $filename, $lang_path . $this->lang_name . '/') === 0)
+ {
+ $language_filename = $phpbb_root_path . $filename;
}
else
{
- $language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . $phpEx;
+ $language_filename = $lang_path . $this->lang_name . '/' . $filename . '.' . $phpEx;
}
if (!file_exists($language_filename))
@@ -1984,24 +2024,24 @@ class user extends session
if ($this->lang_name == 'en')
{
// The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en.
- $language_filename = str_replace($this->lang_path . 'en', $this->lang_path . $this->data['user_lang'], $language_filename);
+ $language_filename = str_replace($lang_path . 'en', $lang_path . $this->data['user_lang'], $language_filename);
trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
}
else if ($this->lang_name == basename($config['default_lang']))
{
// Fall back to the English Language
$this->lang_name = 'en';
- $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
+ $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name);
}
else if ($this->lang_name == $this->data['user_lang'])
{
// Fall back to the board default language
$this->lang_name = basename($config['default_lang']);
- $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
+ $this->set_lang($lang, $help, $lang_file, $use_db, $use_help, $ext_name);
}
// Reset the lang name
- $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
+ $this->lang_name = (file_exists($lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
return;
}
diff --git a/phpBB/includes/template/extension_path_provider.php b/phpBB/includes/template/extension_path_provider.php
new file mode 100644
index 0000000000..0feeaafed0
--- /dev/null
+++ b/phpBB/includes/template/extension_path_provider.php
@@ -0,0 +1,130 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Provides a template locator with core template paths and extension template paths
+*
+* Finds installed template paths and makes them available to the locator.
+*
+* @package phpBB3
+*/
+class phpbb_template_extension_path_provider extends phpbb_extension_provider implements phpbb_template_path_provider_interface
+{
+ /**
+ * Optional prefix for template paths searched within extensions.
+ *
+ * Empty by default. Relative to the extension directory. As an example, it
+ * could be adm/ for admin templates.
+ *
+ * @var string
+ */
+ protected $ext_dir_prefix = '';
+
+ /**
+ * A provider of paths to be searched for templates
+ * @var phpbb_template_path_provider
+ */
+ protected $base_path_provider;
+
+ /**
+ * Constructor stores extension manager
+ *
+ * @param phpbb_extension_manager $extension_manager phpBB extension manager
+ * @param phpbb_template_path_provider $base_path_provider A simple path provider
+ * to provide paths to be located in extensions
+ */
+ public function __construct(phpbb_extension_manager $extension_manager, phpbb_template_path_provider $base_path_provider)
+ {
+ parent::__construct($extension_manager);
+ $this->base_path_provider = $base_path_provider;
+ }
+
+ /**
+ * Sets a prefix for template paths searched within extensions.
+ *
+ * The prefix is inserted between the extension's path e.g. ext/foo/ and
+ * the looked up template path, e.g. styles/bar/template/some.html. So it
+ * should not have a leading slash, but should have a trailing slash.
+ *
+ * @param string $ext_dir_prefix The prefix including trailing slash
+ * @return null
+ */
+ public function set_ext_dir_prefix($ext_dir_prefix)
+ {
+ $this->ext_dir_prefix = $ext_dir_prefix;
+ }
+
+ /**
+ * Finds template paths using the extension manager
+ *
+ * Locates a path (e.g. styles/prosilver/template/) in all active extensions.
+ * Then appends the core template paths based in the current working
+ * directory.
+ *
+ * @return array List of template paths
+ */
+ public function find()
+ {
+ $directories = array();
+
+ $finder = $this->extension_manager->get_finder();
+ foreach ($this->base_path_provider as $path)
+ {
+ if ($path && !phpbb_is_absolute($path))
+ {
+ $directories = array_merge($directories, $finder
+ ->directory('/' . $this->ext_dir_prefix . $path)
+ ->get_directories()
+ );
+ }
+ }
+
+ foreach ($this->base_path_provider as $path)
+ {
+ $directories[] = $path;
+ }
+
+ return $directories;
+ }
+
+ /**
+ * Overwrites the current template names and paths
+ *
+ * @param array $templates An associative map from template names to paths.
+ * The first element is the main template.
+ * If the path is false, it will be generated from
+ * the supplied name.
+ * @param string $style_root_path The root directory for styles identified
+ * by name only.
+ * @return null
+ */
+ public function set_templates(array $templates, $style_root_path)
+ {
+ $this->base_path_provider->set_templates($templates, $style_root_path);
+ $this->items = null;
+ }
+
+ /**
+ * Retrieves the path to the main template passed into set_templates()
+ *
+ * @return string Main template path
+ */
+ public function get_main_template_path()
+ {
+ return $this->base_path_provider->get_main_template_path();
+ }
+}
diff --git a/phpBB/includes/template/locator.php b/phpBB/includes/template/locator.php
index 35e33bb9e6..ee9bfa7d48 100644
--- a/phpBB/includes/template/locator.php
+++ b/phpBB/includes/template/locator.php
@@ -28,62 +28,70 @@ if (!defined('IN_PHPBB'))
class phpbb_template_locator
{
/**
- * @var string Path to directory that templates are stored in.
+ * Paths to directories that templates are stored in.
+ * @var array
*/
- private $root = '';
+ private $roots = array();
/**
- * @var string Path to parent/fallback template directory.
+ * Index of the main template in the roots array
+ * @var int
*/
- private $inherit_root = '';
+ private $main_root_id = 0;
/**
- * @var array Map from handles to source template file paths.
+ * Map from root index to handles to source template file paths.
* Normally it only contains paths for handles that are used
* (or are likely to be used) by the page being rendered and not
* all templates that exist on the filesystem.
+ * @var array
*/
private $files = array();
/**
- * @var array Map from handles to source template file names.
+ * Map from handles to source template file names.
* Covers the same data as $files property but maps to basenames
* instead of paths.
+ * @var array
*/
private $filenames = array();
/**
- * @var array Map from handles to parent/fallback source template
- * file paths. Covers the same data as $files.
+ * Set main template location (must have been added through set_paths first).
+ *
+ * @param string $template_path Path to template directory
+ * @return null
*/
- private $files_inherit = array();
+ public function set_main_template($template)
+ {
+ $this->main_root_id = array_search($template, $this->roots, true);
+ }
/**
- * Set custom template location (able to use directory outside of phpBB).
+ * Sets the list of template paths
*
- * Note: Templates are still compiled to phpBB's cache directory.
+ * These paths will be searched for template files in the provided order.
+ * Paths may be outside of phpBB, but templates loaded from these paths
+ * will still be cached.
*
- * @param string $template_path Path to template directory
- * @param string|bool $fallback_template_path Path to fallback template, or false to disable fallback
+ * @param array $template_paths An array of paths to template directories
+ * @return null
*/
- public function set_custom_template($template_path, $fallback_template_path = false)
+ public function set_paths($template_paths)
{
- // Make sure $template_path has no ending slash
- if (substr($template_path, -1) == '/')
- {
- $template_path = substr($template_path, 0, -1);
- }
-
- $this->root = $template_path;
+ $this->roots = array();
+ $this->files = array();
+ $this->filenames = array();
+ $this->main_root_id = 0;
- if ($fallback_template_path !== false)
+ foreach ($template_paths as $path)
{
- if (substr($fallback_template_path, -1) == '/')
+ // Make sure $path has no ending slash
+ if (substr($path, -1) === '/')
{
- $fallback_template_path = substr($fallback_template_path, 0, -1);
+ $path = substr($path, 0, -1);
}
-
- $this->inherit_root = $fallback_template_path;
+ $this->roots[] = $path;
}
}
@@ -103,11 +111,10 @@ class phpbb_template_locator
}
$this->filename[$handle] = $filename;
- $this->files[$handle] = $this->root . '/' . $filename;
- if ($this->inherit_root)
+ foreach ($this->roots as $root_index => $root)
{
- $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename;
+ $this->files[$root_index][$handle] = $root . '/' . $filename;
}
}
}
@@ -154,12 +161,12 @@ class phpbb_template_locator
public function get_virtual_source_file_for_handle($handle)
{
// If we don't have a file assigned to this handle, die.
- if (!isset($this->files[$handle]))
+ if (!isset($this->files[$this->main_root_id][$handle]))
{
trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR);
}
- $source_file = $this->files[$handle];
+ $source_file = $this->files[$this->main_root_id][$handle];
return $source_file;
}
@@ -182,30 +189,26 @@ class phpbb_template_locator
public function get_source_file_for_handle($handle)
{
// If we don't have a file assigned to this handle, die.
- if (!isset($this->files[$handle]))
+ if (!isset($this->files[$this->main_root_id][$handle]))
{
trigger_error("template locator: No file specified for handle $handle", E_USER_ERROR);
}
- $source_file = $this->files[$handle];
+ // locate a source file that exists
+ $source_file = $this->files[0][$handle];
+ $tried = $source_file;
+ for ($i = 1, $n = count($this->roots); $i < $n && !file_exists($source_file); $i++)
+ {
+ $source_file = $this->files[$i][$handle];
+ $tried .= ', ' . $source_file;
+ }
- // Try and open template for reading
+ // search failed
if (!file_exists($source_file))
{
- if (isset($this->files_inherit[$handle]) && $this->files_inherit[$handle])
- {
- $parent_source_file = $this->files_inherit[$handle];
- if (!file_exists($parent_source_file))
- {
- trigger_error("template locator: Neither $source_file nor $parent_source_file exist", E_USER_ERROR);
- }
- $source_file = $parent_source_file;
- }
- else
- {
- trigger_error("template locator: File $source_file does not exist", E_USER_ERROR);
- }
+ trigger_error("template locator: File for handle $handle does not exist. Could not find: $tried", E_USER_ERROR);
}
+
return $source_file;
}
}
diff --git a/phpBB/includes/template/path_provider.php b/phpBB/includes/template/path_provider.php
new file mode 100644
index 0000000000..c243d1b115
--- /dev/null
+++ b/phpBB/includes/template/path_provider.php
@@ -0,0 +1,102 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Provides a template locator with paths
+*
+* Finds installed template paths and makes them available to the locator.
+*
+* @package phpBB3
+*/
+class phpbb_template_path_provider implements IteratorAggregate, phpbb_template_path_provider_interface
+{
+ protected $main_template_name = '';
+ protected $paths = array();
+
+ /**
+ * Ignores the extension dir prefix
+ *
+ * @param string $ext_dir_prefix The prefix including trailing slash
+ * @return null
+ */
+ public function set_ext_dir_prefix($ext_dir_prefix)
+ {
+ }
+
+ /**
+ * Overwrites the current template names and paths
+ *
+ * The first element of the passed templates map, is considered the main
+ * template and can be retrieved through get_main_template_path().
+ *
+ * @param array $templates An associative map from template names to paths.
+ * The first element is the main template.
+ * If the path is false, it will be generated from
+ * the supplied name.
+ * @param string $style_root_path The root directory for styles identified
+ * by name only.
+ * @return null
+ */
+ public function set_templates(array $templates, $style_root_path)
+ {
+ $this->paths = array();
+
+ foreach ($templates as $name => $path)
+ {
+ if (!$path)
+ {
+ $path = $style_root_path . $this->template_root_for_style($name);
+ }
+
+ $this->paths[] = $path;
+ }
+
+ $this->main_template_path = $this->paths[0];
+ }
+
+ /**
+ * Retrieves the path to the main template passed into set_templates()
+ *
+ * @return string Main template path
+ */
+ public function get_main_template_path()
+ {
+ return $this->main_template_path;
+ }
+
+ /**
+ * Converts a style name to relative (to board root or extension) path to
+ * the style's template files.
+ *
+ * @param $style_name string Style name
+ * @return string Path to style template files
+ */
+ private function template_root_for_style($style_name)
+ {
+ return 'styles/' . $style_name . '/template';
+ }
+
+ /**
+ * Retrieve an iterator over all template paths
+ *
+ * @return ArrayIterator An iterator for the array of template paths
+ */
+ public function getIterator()
+ {
+ return new ArrayIterator($this->paths);
+ }
+}
diff --git a/phpBB/includes/template/path_provider_interface.php b/phpBB/includes/template/path_provider_interface.php
new file mode 100644
index 0000000000..822393a91e
--- /dev/null
+++ b/phpBB/includes/template/path_provider_interface.php
@@ -0,0 +1,54 @@
+<?php
+/**
+*
+* @package phpBB3
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+/**
+* @ignore
+*/
+if (!defined('IN_PHPBB'))
+{
+ exit;
+}
+
+/**
+* Provides a template locator with paths
+*
+* Finds installed template paths and makes them available to the locator.
+*
+* @package phpBB3
+*/
+interface phpbb_template_path_provider_interface extends Traversable
+{
+ /**
+ * Defines a prefix to use for template paths in extensions
+ *
+ * @param string $ext_dir_prefix The prefix including trailing slash
+ * @return null
+ */
+ public function set_ext_dir_prefix($ext_dir_prefix);
+
+ /**
+ * Overwrites the current template names and paths
+ *
+ * @param array $templates An associative map from template names to paths.
+ * The first element is the main template.
+ * If the path is false, it will be generated from
+ * the supplied name.
+ * @param string $style_root_path The root directory for styles identified
+ * by name only.
+ * @return null
+ */
+ public function set_templates(array $templates, $style_root_path);
+
+ /**
+ * Retrieves the path to the main template passed into set_templates()
+ *
+ * @return string Main template path
+ */
+ public function get_main_template_path();
+}
diff --git a/phpBB/includes/template/template.php b/phpBB/includes/template/template.php
index ec5fbe2829..228ea93513 100644
--- a/phpBB/includes/template/template.php
+++ b/phpBB/includes/template/template.php
@@ -63,24 +63,33 @@ class phpbb_template
private $user;
/**
- * @var locator template locator
+ * Template locator
+ * @var phpbb_template_locator
*/
private $locator;
/**
+ * Template path provider
+ * @var phpbb_template_path_provider
+ */
+ private $provider;
+
+ /**
* Constructor.
*
* @param string $phpbb_root_path phpBB root path
* @param user $user current user
* @param phpbb_template_locator $locator template locator
+ * @param phpbb_template_path_provider $provider template path provider
*/
- public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_template_locator $locator)
+ public function __construct($phpbb_root_path, $phpEx, $config, $user, phpbb_template_locator $locator, phpbb_template_path_provider_interface $provider)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->phpEx = $phpEx;
$this->config = $config;
$this->user = $user;
$this->locator = $locator;
+ $this->provider = $provider;
}
/**
@@ -88,25 +97,21 @@ class phpbb_template
*/
public function set_template()
{
- $style_name = $this->user->theme['template_path'];
+ $template_name = $this->user->theme['template_path'];
+ $fallback_name = ($this->user->theme['template_inherits_id']) ? $this->user->theme['template_inherit_path'] : false;
- $relative_template_root = $this->relative_template_root_for_style($style_name);
- $template_root = $this->phpbb_root_path . $relative_template_root;
- if (!file_exists($template_root))
- {
- trigger_error('template locator: Template path could not be found: ' . $relative_template_root, E_USER_ERROR);
- }
-
- if ($this->user->theme['template_inherits_id'])
- {
- $fallback_template_path = $this->phpbb_root_path . $this->relative_template_root_for_style($this->user->theme['template_inherit_path']);
- }
- else
- {
- $fallback_template_path = null;
- }
+ return $this->set_custom_template(false, $template_name, false, $fallback_name);
+ }
- return $this->set_custom_template($template_root, $style_name, $fallback_template_path);
+ /**
+ * Defines a prefix to use for template paths in extensions
+ *
+ * @param string $ext_dir_prefix The prefix including trailing slash
+ * @return null
+ */
+ public function set_ext_dir_prefix($ext_dir_prefix)
+ {
+ $this->provider->set_ext_dir_prefix($ext_dir_prefix);
}
/**
@@ -117,12 +122,22 @@ class phpbb_template
* @param string $template_path Path to template directory
* @param string $template_name Name of template
* @param string $fallback_template_path Path to fallback template
+ * @param string $fallback_template_name Name of fallback template
*/
- public function set_custom_template($template_path, $style_name, $fallback_template_path = false)
+ public function set_custom_template($template_path, $template_name, $fallback_template_path = false, $fallback_template_name = false)
{
- $this->locator->set_custom_template($template_path, $fallback_template_path);
+ $templates = array($template_name => $template_path);
+
+ if ($fallback_template_path !== false)
+ {
+ $templates[$fallback_template_name] = $fallback_template_path;
+ }
+
+ $this->provider->set_templates($templates, $this->phpbb_root_path);
+ $this->locator->set_paths($this->provider);
+ $this->locator->set_main_template($this->provider->get_main_template_path());
- $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $style_name) . '_';
+ $this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_name) . '_';
$this->context = new phpbb_template_context();
@@ -130,18 +145,6 @@ class phpbb_template
}
/**
- * Converts a style name to relative (to board root) path to
- * the style's template files.
- *
- * @param $style_name string Style name
- * @return string Path to style template files
- */
- private function relative_template_root_for_style($style_name)
- {
- return 'styles/' . $style_name . '/template';
- }
-
- /**
* Sets the template filenames for handles.
*
* @param array $filname_array Should be a hash of handle => filename pairs.