1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-06-06 06:25:04 +02:00

Merge remote-tracking branch 'naderman/feature/extension-manager' into develop

* naderman/feature/extension-manager: (67 commits)
  [feature/extension-manager] Removing now unused acp_search code
  [feature/extension-manager] Split disabling extensions up into steps as well
  [feature/extension-manager] Add documentation on caching in ext finder
  [feature/extension-manager] Reference correct new module basenames in install
  [feature/extension-manager] Rename default methods to core methods on finder.
  [feature/extension-manager] Document what the class loader stores in cache
  [feature/extension-manager] Add docblock to cached paths map in class loader
  [feature/extension-manager] Clear up docs of extension related template changes
  [feature/extension-manager] Use "core files" instead of "global files" in docs
  [feature/extension-manager] Add docblocks to new search backend methods
  [feature/extension-manager] Add docblocks to new methods in functions_module
  [feature/extension-manager] Clarify comment on ext meta class instantiator
  [feature/extension-manager] Add more info on suffixes in extension finder
  [feature/extension-manager] Clarify is_dir parameter description
  [feature/extension-manager] Clarify class finding method docblock
  [feature/extension-manager] Correct default path comment & remove double strlen
  [feature/extension-manager] Fix "disbale" typo in comment
  [feature/extension-manager] Properly remove old ACP language loading code
  [feature/extension-manager] Support extensions in subdirectories of ext/
  [feature/extension-manager] Add prefix to extension meta data / install classes
  ...
This commit is contained in:
Igor Wiedler 2011-11-19 01:20:22 +01:00
commit b7c4fb38de
74 changed files with 2574 additions and 534 deletions

@ -52,6 +52,7 @@ $module_id = request_var('i', '');
$mode = request_var('mode', '');
// Set custom template for admin area
$template->set_ext_dir_prefix('adm/');
$template->set_custom_template($phpbb_admin_path . 'style', 'admin');
$template->assign_var('T_ASSETS_PATH', $phpbb_root_path . 'assets');
$template->assign_var('T_TEMPLATE_PATH', $phpbb_admin_path . 'style');

@ -87,13 +87,16 @@ require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
set_error_handler(defined('PHPBB_MSG_HANDLER') ? PHPBB_MSG_HANDLER : 'msg_handler');
// Setup class loader first
$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx);
$class_loader->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx");
$phpbb_class_loader->register();
// set up caching
$cache_factory = new phpbb_cache_factory($acm_type);
$cache = $cache_factory->get_service();
$class_loader->set_cache($cache->get_driver());
$phpbb_class_loader_ext->set_cache($cache->get_driver());
$phpbb_class_loader->set_cache($cache->get_driver());
// Instantiate some basic classes
$request = new phpbb_request();
@ -115,8 +118,12 @@ $config = new phpbb_config_db($db, $cache->get_driver(), CONFIG_TABLE);
set_config(null, null, null, $config);
set_config_count(null, null, null, $config);
$template_locator = new phpbb_template_locator();
$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator);
// load extensions
$phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx", $cache->get_driver());
$phpbb_template_locator = new phpbb_template_locator();
$phpbb_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, $phpbb_template_locator, $phpbb_template_path_provider);
// Add own hook handler
require($phpbb_root_path . 'includes/hooks/index.' . $phpEx);
@ -129,5 +136,5 @@ foreach ($cache->obtain_hooks() as $hook)
if (!$config['use_system_cron'])
{
$cron = new phpbb_cron_manager($phpbb_root_path . 'includes/cron/task', $phpEx, $cache->get_driver());
$cron = new phpbb_cron_manager(new phpbb_cron_task_provider($phpbb_extension_manager), $cache->get_driver());
}

@ -62,7 +62,7 @@ function do_cron($cron_lock, $run_tasks)
if ($config['use_system_cron'])
{
$cron = new phpbb_cron_manager($phpbb_root_path . 'includes/cron/task', $phpEx, $cache->get_driver());
$cron = new phpbb_cron_manager(new phpbb_cron_task_provider($phpbb_extension_manager), $cache->get_driver());
}
else
{

@ -1038,6 +1038,17 @@ function get_schema_struct()
),
);
$schema_data['phpbb_ext'] = array(
'COLUMNS' => array(
'ext_name' => array('VCHAR', ''),
'ext_active' => array('BOOL', 0),
'ext_state' => array('TEXT', ''),
),
'KEYS' => array(
'ext_name' => array('UNIQUE', 'ext_name'),
),
);
$schema_data['phpbb_extensions'] = array(
'COLUMNS' => array(
'extension_id' => array('UINT', NULL, 'auto_increment'),

@ -46,13 +46,16 @@ if (isset($_GET['avatar']))
require($phpbb_root_path . 'includes/functions_download' . '.' . $phpEx);
require($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx);
$class_loader->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx");
$phpbb_class_loader->register();
// set up caching
$cache_factory = new phpbb_cache_factory($acm_type);
$cache = $cache_factory->get_service();
$class_loader->set_cache($cache->get_driver());
$phpbb_class_loader_ext->set_cache($cache->get_driver());
$phpbb_class_loader->set_cache($cache->get_driver());
$request = new phpbb_request();
$db = new $sql_db();
@ -73,6 +76,9 @@ if (isset($_GET['avatar']))
set_config(null, null, null, $config);
set_config_count(null, null, null, $config);
// load extensions
$phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx", $cache->get_driver());
$filename = request_var('avatar', '');
$avatar_group = false;
$exit = false;

@ -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);

@ -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();
while (($file = readdir($dh)) !== false)
$modules = $finder
->extension_suffix('_module')
->extension_directory("/$module_class")
->core_path("includes/$module_class/info/")
->core_prefix($module_class . '_')
->get_classes();
foreach ($modules as $module)
{
// Is module?
if (preg_match('/^' . $module_class . '_.+\.' . $phpEx . '$/', $file))
$info_class = preg_replace('/_module$/', '_info', $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
if (!class_exists($info_class))
{
$class = str_replace(".$phpEx", '', $file) . '_info';
if (!class_exists($class))
$info_class = str_replace("phpbb_{$module_class}_info_", '', $module) . '_info';
if (file_exists($directory . $info_class . '.' . $phpEx))
{
include($directory . $file);
}
// 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;
include($directory . $info_class . '.' . $phpEx);
}
}
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

@ -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;
global $phpbb_root_path, $phpEx, $phpbb_extension_manager;
$search_types = array();
$finder = $phpbb_extension_manager->get_finder();
$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);
sort($search_types);
}
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;

@ -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');

@ -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");
}
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'));
}
}
include($phpbb_root_path . "includes/captcha/plugins/$old_class.$phpEx");
$class = preg_replace('/_plugin$/', '', $old_class);
}
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;

@ -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
{
}

@ -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);

@ -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');

@ -32,138 +32,25 @@ 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)
{

@ -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();
}
}

@ -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;
}
}

@ -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;
}
}

@ -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);
}

@ -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');
}
}

@ -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);
}
}

@ -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;

@ -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;
}
// Create a new instance of the desired module ...
$class_name = $this->p_name;
$this->module = new $class_name($this);
// 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'])
{
$icat = $this->module_ary[$this->active_module_row_id]['parent'];
}
if (!empty($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->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";
$this->module = new $instance($this);
// 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'])
{
$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}";
$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'))
global $phpbb_extension_manager;
$finder = $phpbb_extension_manager->get_finder();
$lang_files = $finder
->prefix('info_' . strtolower($module_class) . '_')
->suffix(".$phpEx")
->extension_directory('/language/' . $user->lang_name)
->core_path('language/' . $user->lang_name . '/mods/')
->find();
foreach ($lang_files as $lang_file => $ext_name)
{
$add_files = array();
$dir = @opendir($user->lang_path . $user->lang_name . '/mods');
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);
}
if (sizeof($add_files))
{
$user->add_lang($add_files);
}
$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 . '_');
}
}

@ -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;

@ -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();

@ -16,17 +16,12 @@ if (!defined('IN_PHPBB'))
exit;
}
/**
* @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;
@ -57,6 +52,16 @@ class fulltext_mysql extends search_backend
$error = false;
}
/**
* 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
*/

@ -16,17 +16,12 @@ if (!defined('IN_PHPBB'))
exit;
}
/**
* @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,10 +51,19 @@ 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.
*

@ -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
{
$language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . $phpEx;
$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 = $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;
}

@ -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();
}
}

@ -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.
*/
private $files_inherit = array();
/**
* Set custom template location (able to use directory outside of phpBB).
*
* Note: Templates are still compiled to phpBB's cache directory.
* Set main template location (must have been added through set_paths first).
*
* @param string $template_path Path to template directory
* @param string|bool $fallback_template_path Path to fallback template, or false to disable fallback
* @return null
*/
public function set_custom_template($template_path, $fallback_template_path = false)
public function set_main_template($template)
{
// Make sure $template_path has no ending slash
if (substr($template_path, -1) == '/')
{
$template_path = substr($template_path, 0, -1);
}
$this->main_root_id = array_search($template, $this->roots, true);
}
$this->root = $template_path;
/**
* Sets the list of template paths
*
* 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 array $template_paths An array of paths to template directories
* @return null
*/
public function set_paths($template_paths)
{
$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;
}
}

@ -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);
}
}

@ -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();
}

@ -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);
}
return $this->set_custom_template(false, $template_name, false, $fallback_name);
}
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($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,30 +122,28 @@ 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);
$this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $style_name) . '_';
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('_', '-', $template_name) . '_';
$this->context = new phpbb_template_context();
return true;
}
/**
* 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.
*

@ -104,14 +104,21 @@ if (!defined('LOGIN_ATTEMPT_TABLE'))
{
define('LOGIN_ATTEMPT_TABLE', $table_prefix . 'login_attempts');
}
if (!defined('EXT_TABLE'))
{
define('EXT_TABLE', $table_prefix . 'ext');
}
$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx);
$class_loader->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx");
$phpbb_class_loader->register();
// set up caching
$cache_factory = new phpbb_cache_factory($acm_type);
$cache = $cache_factory->get_service();
$class_loader->set_cache($cache->get_driver());
$phpbb_class_loader_ext->set_cache($cache->get_driver());
$phpbb_class_loader->set_cache($cache->get_driver());
$request = new phpbb_request();
$user = new user();
@ -671,7 +678,13 @@ function _write_result($no_updates, $errored, $error_ary)
function _add_modules($modules_to_install)
{
global $phpbb_root_path, $phpEx, $db;
global $phpbb_root_path, $phpEx, $db, $phpbb_extension_manager;
// modules require an extension manager
if (empty($phpbb_extension_manager))
{
$phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx");
}
include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
@ -1048,6 +1061,18 @@ function database_update_info()
// Changes from 3.1.0-dev to 3.1.0-A1
'3.1.0-dev' => array(
'add_tables' => array(
EXT_TABLE => array(
'COLUMNS' => array(
'ext_name' => array('VCHAR', ''),
'ext_active' => array('BOOL', 0),
'ext_state' => array('TEXT', ''),
),
'KEYS' => array(
'ext_name' => array('UNIQUE', 'ext_name'),
),
),
),
'add_columns' => array(
GROUPS_TABLE => array(
'group_teampage' => array('UINT', 0, 'after' => 'group_legend'),
@ -2095,8 +2120,49 @@ function change_database_data(&$no_updates, $version)
// Changes from 3.1.0-dev to 3.1.0-A1
case '3.1.0-dev':
set_config('load_jquery_cdn', 0);
set_config('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js');
// rename all module basenames to full classname
$sql = 'SELECT module_id, module_basename, module_class
FROM ' . MODULES_TABLE;
$result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result))
{
$module_id = (int) $row['module_id'];
unset($row['module_id']);
if (!empty($row['module_basename']) && !empty($row['module_class']))
{
// all the class names start with class name or with phpbb_ for auto loading
if (strpos($row['module_basename'], $row['module_class'] . '_') !== 0 &&
strpos($row['module_basename'], 'phpbb_') !== 0)
{
$row['module_basename'] = $row['module_class'] . '_' . $row['module_basename'];
$sql_update = $db->sql_build_array('UPDATE', $row);
$sql = 'UPDATE ' . MODULES_TABLE . '
SET ' . $sql_update . '
WHERE module_id = ' . $module_id;
_sql($sql, $errored, $error_ary);
}
}
}
$db->sql_freeresult($result);
if (substr($config['search_type'], 0, 6) !== 'phpbb_')
{
// try to guess the new auto loaded search class name
// works for native and mysql fulltext
set_config('search_type', 'phpbb_search_' . $config['search_type']);
}
if (!isset($config['load_jquery_cdn']))
{
set_config('load_jquery_cdn', 0);
set_config('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js');
}
if (!isset($config['use_system_cron']))
{
@ -2163,14 +2229,14 @@ function change_database_data(&$no_updates, $version)
// Install modules
$modules_to_install = array(
'position' => array(
'base' => 'groups',
'base' => 'acp_groups',
'class' => 'acp',
'title' => 'ACP_GROUPS_POSITION',
'auth' => 'acl_a_group',
'cat' => 'ACP_GROUPS',
),
'manage' => array(
'base' => 'attachments',
'base' => 'acp_attachments',
'class' => 'acp',
'title' => 'ACP_MANAGE_ATTACHMENTS',
'auth' => 'acl_a_attach',
@ -2179,7 +2245,7 @@ function change_database_data(&$no_updates, $version)
);
_add_modules($modules_to_install);
$sql = 'DELETE FROM ' . MODULES_TABLE . "
WHERE module_basename = 'styles' AND module_mode = 'imageset'";
_sql($sql, $errored, $error_ary);

@ -82,13 +82,16 @@ include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
include($phpbb_root_path . 'includes/utf/utf_tools.' . $phpEx);
require($phpbb_root_path . 'includes/functions_install.' . $phpEx);
$class_loader = new phpbb_class_loader($phpbb_root_path, '.' . $phpEx);
$class_loader->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".$phpEx");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".$phpEx");
$phpbb_class_loader->register();
// set up caching
$cache_factory = new phpbb_cache_factory('file');
$cache = $cache_factory->get_service();
$class_loader->set_cache($cache->get_driver());
$phpbb_class_loader_ext->set_cache($cache->get_driver());
$phpbb_class_loader->set_cache($cache->get_driver());
$request = new phpbb_request();
@ -199,8 +202,10 @@ $config = new phpbb_config(array(
'load_tplcompile' => '1'
));
$template_locator = new phpbb_template_locator();
$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $template_locator);
$phpbb_template_locator = new phpbb_template_locator();
$phpbb_template_path_provider = new phpbb_template_path_provider();
$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $phpbb_template_locator, $phpbb_template_path_provider);
$template->set_ext_dir_prefix('adm/');
$template->set_custom_template('../adm/style', 'admin');
$template->assign_var('T_ASSETS_PATH', '../assets');
$template->assign_var('T_TEMPLATE_PATH', '../adm/style');

@ -1445,7 +1445,7 @@ class install_install extends module
set_config_count(null, null, null, $config);
$error = false;
$search = new fulltext_native($error);
$search = new phpbb_search_fulltext_native($error);
$sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id
FROM ' . POSTS_TABLE;
@ -1463,7 +1463,13 @@ class install_install extends module
*/
function add_modules($mode, $sub)
{
global $db, $lang, $phpbb_root_path, $phpEx;
global $db, $lang, $phpbb_root_path, $phpEx, $phpbb_extension_manager;
// modules require an extension manager
if (empty($phpbb_extension_manager))
{
$phpbb_extension_manager = new phpbb_extension_manager($db, EXT_TABLE, $phpbb_root_path, ".$phpEx");
}
include_once($phpbb_root_path . 'includes/acp/acp_modules.' . $phpEx);
@ -1578,7 +1584,7 @@ class install_install extends module
// Move main module 4 up...
$sql = 'SELECT *
FROM ' . MODULES_TABLE . "
WHERE module_basename = 'main'
WHERE module_basename = 'acp_main'
AND module_class = 'acp'
AND module_mode = 'main'";
$result = $db->sql_query($sql);
@ -1590,7 +1596,7 @@ class install_install extends module
// Move permissions intro screen module 4 up...
$sql = 'SELECT *
FROM ' . MODULES_TABLE . "
WHERE module_basename = 'permissions'
WHERE module_basename = 'acp_permissions'
AND module_class = 'acp'
AND module_mode = 'intro'";
$result = $db->sql_query($sql);
@ -1602,7 +1608,7 @@ class install_install extends module
// Move manage users screen module 5 up...
$sql = 'SELECT *
FROM ' . MODULES_TABLE . "
WHERE module_basename = 'users'
WHERE module_basename = 'acp_users'
AND module_class = 'acp'
AND module_mode = 'overview'";
$result = $db->sql_query($sql);
@ -1617,7 +1623,7 @@ class install_install extends module
// Move attachment module 4 down...
$sql = 'SELECT *
FROM ' . MODULES_TABLE . "
WHERE module_basename = 'attachments'
WHERE module_basename = 'ucp_attachments'
AND module_class = 'ucp'
AND module_mode = 'attachments'";
$result = $db->sql_query($sql);

@ -282,6 +282,15 @@ BEGIN
END;;
# Table: 'phpbb_ext'
CREATE TABLE phpbb_ext (
ext_name VARCHAR(255) CHARACTER SET NONE DEFAULT '' NOT NULL,
ext_active INTEGER DEFAULT 0 NOT NULL,
ext_state BLOB SUB_TYPE TEXT CHARACTER SET NONE DEFAULT '' NOT NULL
);;
CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext(ext_name);;
# Table: 'phpbb_extensions'
CREATE TABLE phpbb_extensions (
extension_id INTEGER NOT NULL,

@ -360,6 +360,20 @@ CREATE INDEX [save_time] ON [phpbb_drafts]([save_time]) ON [PRIMARY]
GO
/*
Table: 'phpbb_ext'
*/
CREATE TABLE [phpbb_ext] (
[ext_name] [varchar] (255) DEFAULT ('') NOT NULL ,
[ext_active] [int] DEFAULT (0) NOT NULL ,
[ext_state] [varchar] (8000) DEFAULT ('') NOT NULL
) ON [PRIMARY]
GO
CREATE UNIQUE INDEX [ext_name] ON [phpbb_ext]([ext_name]) ON [PRIMARY]
GO
/*
Table: 'phpbb_extensions'
*/

@ -192,6 +192,15 @@ CREATE TABLE phpbb_drafts (
);
# Table: 'phpbb_ext'
CREATE TABLE phpbb_ext (
ext_name varbinary(255) DEFAULT '' NOT NULL,
ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
ext_state blob NOT NULL,
UNIQUE ext_name (ext_name)
);
# Table: 'phpbb_extensions'
CREATE TABLE phpbb_extensions (
extension_id mediumint(8) UNSIGNED NOT NULL auto_increment,

@ -192,6 +192,15 @@ CREATE TABLE phpbb_drafts (
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
# Table: 'phpbb_ext'
CREATE TABLE phpbb_ext (
ext_name varchar(255) DEFAULT '' NOT NULL,
ext_active tinyint(1) UNSIGNED DEFAULT '0' NOT NULL,
ext_state text NOT NULL,
UNIQUE ext_name (ext_name)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
# Table: 'phpbb_extensions'
CREATE TABLE phpbb_extensions (
extension_id mediumint(8) UNSIGNED NOT NULL auto_increment,

@ -409,6 +409,18 @@ END;
/
/*
Table: 'phpbb_ext'
*/
CREATE TABLE phpbb_ext (
ext_name varchar2(255) DEFAULT '' ,
ext_active number(1) DEFAULT '0' NOT NULL,
ext_state clob DEFAULT '' ,
CONSTRAINT u_phpbb_ext_name UNIQUE (ext_name)
)
/
/*
Table: 'phpbb_extensions'
*/

@ -314,6 +314,17 @@ CREATE TABLE phpbb_drafts (
CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time);
/*
Table: 'phpbb_ext'
*/
CREATE TABLE phpbb_ext (
ext_name varchar(255) DEFAULT '' NOT NULL,
ext_active INT2 DEFAULT '0' NOT NULL CHECK (ext_active >= 0),
ext_state varchar(8000) DEFAULT '' NOT NULL
);
CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name);
/*
Table: 'phpbb_extensions'
*/

@ -226,7 +226,7 @@ INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_block_size'
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_gc', '7200');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_interval', '0');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_anonymous_interval', '0');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', 'fulltext_native');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_type', 'phpbb_search_fulltext_native');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('search_store_results', '1800');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_deny', '1');
INSERT INTO phpbb_config (config_name, config_value) VALUES ('secure_allow_empty_referer', '1');

@ -187,6 +187,15 @@ CREATE TABLE phpbb_drafts (
CREATE INDEX phpbb_drafts_save_time ON phpbb_drafts (save_time);
# Table: 'phpbb_ext'
CREATE TABLE phpbb_ext (
ext_name varchar(255) NOT NULL DEFAULT '',
ext_active INTEGER UNSIGNED NOT NULL DEFAULT '0',
ext_state text(65535) NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX phpbb_ext_ext_name ON phpbb_ext (ext_name);
# Table: 'phpbb_extensions'
CREATE TABLE phpbb_extensions (
extension_id INTEGER PRIMARY KEY NOT NULL ,

@ -73,7 +73,7 @@ switch ($search_id)
login_box('', $user->lang['LOGIN_EXPLAIN_UNREADSEARCH']);
}
break;
// The "new posts" search uses user_lastvisit which is user based, so it should require user to log in.
case 'newposts':
if ($user->data['user_id'] == ANONYMOUS)
@ -81,7 +81,7 @@ switch ($search_id)
login_box('', $user->lang['LOGIN_EXPLAIN_NEWPOSTS']);
}
break;
default:
// There's nothing to do here for now ;)
break;
@ -273,15 +273,12 @@ if ($keywords || $author || $author_id || $search_id || $submit)
}
// Select which method we'll use to obtain the post_id or topic_id information
$search_type = basename($config['search_type']);
$search_type = $config['search_type'];
if (!file_exists($phpbb_root_path . 'includes/search/' . $search_type . '.' . $phpEx))
if (!class_exists($search_type))
{
trigger_error('NO_SUCH_SEARCH_MODULE');
}
require("{$phpbb_root_path}includes/search/$search_type.$phpEx");
// We do some additional checks in the module to ensure it can actually be utilised
$error = false;
$search = new $search_type($error);

@ -32,8 +32,10 @@ else
require_once $phpbb_root_path . 'includes/constants.php';
require_once $phpbb_root_path . 'includes/class_loader.' . $phpEx;
$class_loader = new phpbb_class_loader($phpbb_root_path, '.php');
$class_loader->register();
$phpbb_class_loader_ext = new phpbb_class_loader('phpbb_ext_', $phpbb_root_path . 'ext/', ".php");
$phpbb_class_loader_ext->register();
$phpbb_class_loader = new phpbb_class_loader('phpbb_', $phpbb_root_path . 'includes/', ".php");
$phpbb_class_loader->register();
require_once 'test_framework/phpbb_test_case_helpers.php';
require_once 'test_framework/phpbb_test_case.php';

@ -13,20 +13,26 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase
{
public function setUp()
{
global $class_loader;
$class_loader->unregister();
global $phpbb_class_loader;
$phpbb_class_loader->unregister();
global $phpbb_class_loader_ext;
$phpbb_class_loader_ext->unregister();
}
public function tearDown()
{
global $class_loader;
$class_loader->register();
global $phpbb_class_loader_ext;
$phpbb_class_loader_ext->register();
global $phpbb_class_loader;
$phpbb_class_loader->register();
}
public function test_resolve_path()
{
$prefix = dirname(__FILE__) . '/';
$class_loader = new phpbb_class_loader($prefix);
$class_loader = new phpbb_class_loader('phpbb_', $prefix . 'includes/');
$prefix .= 'includes/';
@ -60,11 +66,15 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase
public function test_resolve_cached()
{
$cacheMap = array('class_loader' => array('phpbb_a_cached_name' => 'a/cached_name'));
$cache = new phpbb_mock_cache($cacheMap);
$cache_map = array(
'class_loader_phpbb_' => array('phpbb_a_cached_name' => 'a/cached_name'),
'class_loader_phpbb_ext_' => array('phpbb_ext_foo' => 'foo'),
);
$cache = new phpbb_mock_cache($cache_map);
$prefix = dirname(__FILE__) . '/';
$class_loader = new phpbb_class_loader($prefix, '.php', $cache);
$class_loader = new phpbb_class_loader('phpbb_', $prefix . 'includes/', '.php', $cache);
$class_loader_ext = new phpbb_class_loader('phpbb_ext_', $prefix . 'includes/', '.php', $cache);
$prefix .= 'includes/';
@ -74,13 +84,22 @@ class phpbb_class_loader_test extends PHPUnit_Framework_TestCase
'Class in a directory'
);
$this->assertFalse($class_loader->resolve_path('phpbb_ext_foo'));
$this->assertFalse($class_loader_ext->resolve_path('phpbb_a_cached_name'));
$this->assertEquals(
$prefix . 'a/cached_name.php',
$class_loader->resolve_path('phpbb_a_cached_name'),
'Class in a directory'
'Cached class found'
);
$cacheMap['class_loader']['phpbb_dir_class_name'] = 'dir/class_name';
$cache->check($this, $cacheMap);
$this->assertEquals(
$prefix . 'foo.php',
$class_loader_ext->resolve_path('phpbb_ext_foo'),
'Cached class found in alternative loader'
);
$cache_map['class_loader_phpbb_']['phpbb_dir_class_name'] = 'dir/class_name';
$cache->check($this, $cache_map);
}
}

@ -7,7 +7,7 @@
*
*/
class phpbb_cron_task_testmod_dummy_task extends phpbb_cron_task_base
class phpbb_ext_testext_cron_dummy_task extends phpbb_cron_task_base
{
public static $was_run = 0;

@ -0,0 +1,23 @@
<?php
/**
*
* @package testing
* @copyright (c) 2010 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
class phpbb_cron_task_core_dummy_task extends phpbb_cron_task_base
{
public static $was_run = 0;
public function run()
{
self::$was_run++;
}
public function should_run()
{
return true;
}
}

@ -7,7 +7,7 @@
*
*/
class phpbb_cron_task_testmod_second_dummy_task extends phpbb_cron_task_base
class phpbb_cron_task_core_second_dummy_task extends phpbb_cron_task_base
{
public static $was_run = 0;

@ -7,25 +7,24 @@
*
*/
require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/task/testmod/dummy_task.php';
require_once dirname(__FILE__) . '/task/testmod/second_dummy_task.php';
require_once dirname(__FILE__) . '/task2/testmod/simple_ready.php';
require_once dirname(__FILE__) . '/task2/testmod/simple_not_runnable.php';
require_once dirname(__FILE__) . '/task2/testmod/simple_should_not_run.php';
require_once dirname(__FILE__) . '/../mock/extension_manager.php';
require_once dirname(__FILE__) . '/includes/cron/task/core/dummy_task.php';
require_once dirname(__FILE__) . '/includes/cron/task/core/second_dummy_task.php';
require_once dirname(__FILE__) . '/ext/testext/cron/dummy_task.php';
require_once dirname(__FILE__) . '/tasks/simple_ready.php';
require_once dirname(__FILE__) . '/tasks/simple_not_runnable.php';
require_once dirname(__FILE__) . '/tasks/simple_should_not_run.php';
class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->manager = new phpbb_cron_manager(dirname(__FILE__) . '/task/', 'php');
$this->task_name = 'phpbb_cron_task_testmod_dummy_task';
}
public function test_manager_finds_shipped_tasks()
{
$tasks = $this->manager->find_cron_task_names();
$this->assertEquals(2, sizeof($tasks));
$this->manager = new phpbb_cron_manager(array(
'phpbb_cron_task_core_dummy_task',
'phpbb_cron_task_core_second_dummy_task',
'phpbb_ext_testext_cron_dummy_task',
));
$this->task_name = 'phpbb_cron_task_core_dummy_task';
}
public function test_manager_finds_shipped_task_by_name()
@ -45,7 +44,7 @@ class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase
public function test_manager_finds_all_ready_tasks()
{
$tasks = $this->manager->find_all_ready_tasks();
$this->assertEquals(2, sizeof($tasks));
$this->assertEquals(3, sizeof($tasks));
}
public function test_manager_finds_one_ready_task()
@ -54,21 +53,16 @@ class phpbb_cron_manager_test extends PHPUnit_Framework_TestCase
$this->assertInstanceOf('phpbb_cron_task_wrapper', $task);
}
public function test_manager_finds_all_ready_tasks_cached()
{
$cache = new phpbb_mock_cache(array('_cron_tasks' => array($this->task_name)));
$manager = new phpbb_cron_manager(dirname(__FILE__) . '/../../phpBB/', 'php', $cache);
$tasks = $manager->find_all_ready_tasks();
$this->assertEquals(1, sizeof($tasks));
}
public function test_manager_finds_only_ready_tasks()
{
$manager = new phpbb_cron_manager(dirname(__FILE__) . '/task2/', 'php');
$manager = new phpbb_cron_manager(array(
'phpbb_cron_task_core_simple_ready',
'phpbb_cron_task_core_simple_not_runnable',
'phpbb_cron_task_core_simple_should_not_run',
));
$tasks = $manager->find_all_ready_tasks();
$task_names = $this->tasks_to_names($tasks);
$this->assertEquals(array('phpbb_cron_task_testmod_simple_ready'), $task_names);
$this->assertEquals(array('phpbb_cron_task_core_simple_ready'), $task_names);
}
private function tasks_to_names($tasks)

@ -1,8 +0,0 @@
<?php
class phpbb_cron_task_testmod_simple_ready extends phpbb_cron_task_base
{
public function run()
{
}
}

@ -0,0 +1,43 @@
<?php
/**
*
* @package testing
* @copyright (c) 2010 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
require_once dirname(__FILE__) . '/../mock/extension_manager.php';
class phpbb_cron_task_provider_test extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->extension_manager = new phpbb_mock_extension_manager(
dirname(__FILE__) . '/',
array(
'testext' => array(
'ext_name' => 'testext',
'ext_active' => true,
'ext_path' => 'ext/testext/'
),
));
$this->provider = new phpbb_cron_task_provider($this->extension_manager);
}
public function test_manager_finds_shipped_tasks()
{
$tasks = array();
foreach ($this->provider as $task)
{
$tasks[] = $task;
}
sort($tasks);
$this->assertEquals(array(
'phpbb_cron_task_core_dummy_task',
'phpbb_cron_task_core_second_dummy_task',
'phpbb_ext_testext_cron_dummy_task',
), $tasks);
}
}

@ -1,6 +1,6 @@
<?php
class phpbb_cron_task_testmod_simple_not_runnable extends phpbb_cron_task_base
class phpbb_cron_task_core_simple_not_runnable extends phpbb_cron_task_base
{
public function run()
{

@ -0,0 +1,8 @@
<?php
class phpbb_cron_task_core_simple_ready extends phpbb_cron_task_base
{
public function run()
{
}
}

@ -1,6 +1,6 @@
<?php
class phpbb_cron_task_testmod_simple_should_not_run extends phpbb_cron_task_base
class phpbb_cron_task_core_simple_should_not_run extends phpbb_cron_task_base
{
public function run()
{

@ -0,0 +1,24 @@
<?php
class phpbb_ext_bar_ext extends phpbb_extension_base
{
static public $state;
public function enable_step($old_state)
{
// run 4 steps, then quit
if ($old_state === 4)
{
return false;
}
if ($old_state === false)
{
$old_state = 0;
}
self::$state = ++$old_state;
return self::$state;
}
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_bar_my_hidden_class
{
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_foo_a_class
{
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_foo_b_class
{
}

@ -0,0 +1,13 @@
<?php
class phpbb_ext_foo_ext extends phpbb_extension_base
{
static public $disabled;
public function disable_step($old_state)
{
self::$disabled = true;
return false;
}
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_foo_sub_type_alternative
{
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_foo_type_alternative
{
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_foo_typewrong_error
{
}

13
tests/extension/ext/vendor/moo/ext.php vendored Normal file

@ -0,0 +1,13 @@
<?php
class phpbb_ext_vendor_moo_ext extends phpbb_extension_base
{
static public $purged;
public function purge_step($old_state)
{
self::$purged = true;
return false;
}
}

@ -0,0 +1,5 @@
<?php
class phpbb_ext_vendor_moo_feature_class
{
}

@ -0,0 +1,206 @@
<?php
/**
*
* @package testing
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/../mock/extension_manager.php';
class phpbb_extension_finder_test extends phpbb_test_case
{
protected $extension_manager;
protected $finder;
public function setUp()
{
$this->extension_manager = new phpbb_mock_extension_manager(
dirname(__FILE__) . '/',
array(
'foo' => array(
'ext_name' => 'foo',
'ext_active' => '1',
'ext_path' => 'ext/foo/',
),
'bar' => array(
'ext_name' => 'bar',
'ext_active' => '1',
'ext_path' => 'ext/bar/',
),
));
$this->finder = $this->extension_manager->get_finder();
}
public function test_suffix_get_classes()
{
$classes = $this->finder
->core_path('includes/default/')
->extension_suffix('_class')
->get_classes();
sort($classes);
$this->assertEquals(
array(
'phpbb_default_implementation',
'phpbb_ext_bar_my_hidden_class',
'phpbb_ext_foo_a_class',
'phpbb_ext_foo_b_class',
),
$classes
);
}
public function test_get_directories()
{
$dirs = $this->finder
->directory('/type')
->get_directories();
sort($dirs);
$this->assertEquals(array(
dirname(__FILE__) . '/ext/foo/type/',
), $dirs);
}
public function test_prefix_get_directories()
{
$dirs = $this->finder
->prefix('t')
->get_directories();
sort($dirs);
$this->assertEquals(array(
dirname(__FILE__) . '/ext/foo/sub/type/',
dirname(__FILE__) . '/ext/foo/type/',
dirname(__FILE__) . '/ext/foo/typewrong/',
), $dirs);
}
public function test_prefix_get_classes()
{
$classes = $this->finder
->core_path('includes/default/')
->extension_prefix('hidden_')
->get_classes();
sort($classes);
$this->assertEquals(
array(
'phpbb_default_implementation',
'phpbb_ext_bar_my_hidden_class',
),
$classes
);
}
public function test_directory_get_classes()
{
$classes = $this->finder
->core_path('includes/default/')
->extension_directory('type')
->get_classes();
sort($classes);
$this->assertEquals(
array(
'phpbb_default_implementation',
'phpbb_ext_foo_sub_type_alternative',
'phpbb_ext_foo_type_alternative',
),
$classes
);
}
public function test_absolute_directory_get_classes()
{
$classes = $this->finder
->directory('/type/')
->get_classes();
sort($classes);
$this->assertEquals(
array(
'phpbb_ext_foo_type_alternative',
),
$classes
);
}
public function test_sub_directory_get_classes()
{
$classes = $this->finder
->directory('/sub/type')
->get_classes();
sort($classes);
$this->assertEquals(
array(
'phpbb_ext_foo_sub_type_alternative',
),
$classes
);
}
public function test_get_classes_create_cache()
{
$cache = new phpbb_mock_cache;
$finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/', $cache, '.php', '_custom_cache_name');
$files = $finder->suffix('_class.php')->get_files();
$expected_files = array(
'ext/bar/my/hidden_class.php' => 'bar',
'ext/foo/a_class.php' => 'foo',
'ext/foo/b_class.php' => 'foo',
);
$query = array(
'core_path' => false,
'core_suffix' => '_class.php',
'core_prefix' => false,
'core_directory' => false,
'extension_suffix' => '_class.php',
'extension_prefix' => false,
'extension_directory' => false,
'is_dir' => false,
);
$cache->checkAssociativeVar($this, '_custom_cache_name', array(
md5(serialize($query)) => $expected_files,
), false);
}
public function test_cached_get_files()
{
$query = array(
'core_path' => 'includes/foo',
'core_suffix' => false,
'core_prefix' => false,
'core_directory' => 'bar',
'extension_suffix' => false,
'extension_prefix' => false,
'extension_directory' => false,
'is_dir' => false,
);
$finder = new phpbb_extension_finder($this->extension_manager, dirname(__FILE__) . '/', new phpbb_mock_cache(array(
'_ext_finder' => array(
md5(serialize($query)) => array('file_name' => 'extension'),
),
)));
$classes = $finder
->core_path($query['core_path'])
->core_directory($query['core_directory'])
->get_files();
sort($classes);
$this->assertEquals(
array(dirname(__FILE__) . '/file_name'),
$classes
);
}
}

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dataset>
<table name="phpbb_ext">
<column>ext_name</column>
<column>ext_active</column>
<row>
<value>foo</value>
<value>1</value>
</row>
<row>
<value>vendor/moo</value>
<value>0</value>
</row>
</table>
</dataset>

@ -0,0 +1,5 @@
<?php
class phpbb_default_impl_class implements phpbb_default_interface
{
}

@ -0,0 +1,102 @@
<?php
/**
*
* @package testing
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
require_once dirname(__FILE__) . '/../mock/cache.php';
require_once dirname(__FILE__) . '/ext/bar/ext.php';
require_once dirname(__FILE__) . '/ext/foo/ext.php';
require_once dirname(__FILE__) . '/ext/vendor/moo/ext.php';
class phpbb_extension_manager_test extends phpbb_database_test_case
{
protected $extension_manager;
protected $class_loader;
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/extensions.xml');
}
protected function setUp()
{
parent::setUp();
$this->extension_manager = new phpbb_extension_manager(
$this->new_dbal(),
'phpbb_ext',
dirname(__FILE__) . '/',
'.php',
new phpbb_mock_cache
);
}
public function test_available()
{
$this->assertEquals(array('bar', 'foo', 'vendor/moo'), array_keys($this->extension_manager->all_available()));
}
public function test_enabled()
{
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled()));
}
public function test_configured()
{
$this->assertEquals(array('foo', 'vendor/moo'), array_keys($this->extension_manager->all_configured()));
}
public function test_enable()
{
phpbb_ext_bar_ext::$state = 0;
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled()));
$this->extension_manager->enable('bar');
$this->assertEquals(array('bar', 'foo'), array_keys($this->extension_manager->all_enabled()));
$this->assertEquals(array('bar', 'foo', 'vendor/moo'), array_keys($this->extension_manager->all_configured()));
$this->assertEquals(4, phpbb_ext_bar_ext::$state);
}
public function test_disable()
{
phpbb_ext_foo_ext::$disabled = false;
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled()));
$this->extension_manager->disable('foo');
$this->assertEquals(array(), array_keys($this->extension_manager->all_enabled()));
$this->assertEquals(array('foo', 'vendor/moo'), array_keys($this->extension_manager->all_configured()));
$this->assertTrue(phpbb_ext_foo_ext::$disabled);
}
public function test_purge()
{
phpbb_ext_vendor_moo_ext::$purged = false;
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled()));
$this->assertEquals(array('foo', 'vendor/moo'), array_keys($this->extension_manager->all_configured()));
$this->extension_manager->purge('vendor/moo');
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_enabled()));
$this->assertEquals(array('foo'), array_keys($this->extension_manager->all_configured()));
$this->assertTrue(phpbb_ext_vendor_moo_ext::$purged);
}
public function test_enabled_no_cache()
{
$extension_manager = new phpbb_extension_manager(
$this->new_dbal(),
'phpbb_ext',
dirname(__FILE__) . '/',
'.php'
);
$this->assertEquals(array('foo'), array_keys($extension_manager->all_enabled()));
}
}

@ -59,6 +59,21 @@ class phpbb_mock_cache implements phpbb_cache_driver_interface
$test->assertEquals($data, $this->data[$var_name]);
}
public function checkAssociativeVar(PHPUnit_Framework_Assert $test, $var_name, $data, $sort = true)
{
$test->assertTrue(isset($this->data[$var_name]));
if ($sort)
{
foreach ($this->data[$var_name] as &$content)
{
sort($content);
}
}
$test->assertEquals($data, $this->data[$var_name]);
}
public function checkVarUnset(PHPUnit_Framework_Assert $test, $var_name)
{
$test->assertFalse(isset($this->data[$var_name]));

@ -0,0 +1,18 @@
<?php
/**
*
* @package testing
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/
class phpbb_mock_extension_manager extends phpbb_extension_manager
{
public function __construct($phpbb_root_path, $extensions = array())
{
$this->phpbb_root_path = $phpbb_root_path;
$this->phpEx = '.php';
$this->extensions = $extensions;
}
}

@ -69,7 +69,8 @@ class phpbb_template_template_inheritance_test extends phpbb_template_template_t
$this->template_path = dirname(__FILE__) . '/templates';
$this->parent_template_path = dirname(__FILE__) . '/parent_templates';
$this->template_locator = new phpbb_template_locator();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator);
$this->template_provider = new phpbb_template_path_provider();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator, $this->template_provider);
$this->template->set_custom_template($this->template_path, 'tests', $this->parent_template_path);
}
}

@ -277,7 +277,7 @@ class phpbb_template_template_test extends phpbb_template_template_test_case
$this->template->set_filenames(array('test' => $filename));
$this->assertFileNotExists($this->template_path . '/' . $filename, 'Testing missing file, file cannot exist');
$expecting = sprintf('template locator: File %s does not exist', realpath($this->template_path . '/../') . '/templates/' . $filename);
$expecting = sprintf('template locator: File for handle test does not exist. Could not find: %s', realpath($this->template_path . '/../') . '/templates/' . $filename);
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test');

@ -8,12 +8,14 @@
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
require_once dirname(__FILE__) . '/../mock/extension_manager.php';
class phpbb_template_template_test_case extends phpbb_test_case
{
protected $template;
protected $template_path;
protected $template_locator;
protected $template_provider;
// Keep the contents of the cache for debugging?
const PRESERVE_CACHE = true;
@ -57,7 +59,8 @@ class phpbb_template_template_test_case extends phpbb_test_case
$this->template_path = dirname(__FILE__) . '/templates';
$this->template_locator = new phpbb_template_locator();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator);
$this->template_provider = new phpbb_template_path_provider();
$this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator, $this->template_provider);
$this->template->set_custom_template($this->template_path, 'tests');
}