1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-08-08 17:56:52 +02:00

Merge branch 'develop' into ticket/11215

* develop: (176 commits)
  [feature/bootstrap-dic] Bootstrap container from config.php
  [ticket/11548] Use new static methods for request and submit
  [ticket/10772] Updating tests
  [ticket/10772] Remove 3.1 code
  [ticket/11388] Add newlines at EOF
  [ticket/11388] INCLUDECSS
  [ticket/11548] Run array_map on complete error array and not just colour_error
  [ticket/11644] Skip phpbb_dbal_order_lower_test on MySQL 5.6
  [ticket/11388] Do not append assets_version if using remote path (e.g. http)
  [ticket/11388] Fix typo
  [ticket/11388] Remove typehints (causing tests to fail)
  [ticket/11388] Disable cache if IN_INSTALL defined
  [ticket/11388] Do not modify by reference
  [ticket/11388] typehits
  [ticket/11388] INCLUDEJS test for //(url)
  [ticket/11388] INCLUDEJS supports //(url)
  [ticket/11388] Fixing includejs test
  [ticket/11388] includejs inherit from includeasset
  [feature/twig] Unit tests for includejs
  [ticket/8319] Add migration file for update change
  ...
This commit is contained in:
David King
2013-07-11 20:43:41 -04:00
94 changed files with 3111 additions and 2709 deletions

View File

@@ -112,8 +112,8 @@ class acp_bbcodes
{
$template->assign_block_vars('token', array(
'TOKEN' => '{' . $token . '}',
'EXPLAIN' => $token_explain)
);
'EXPLAIN' => ($token === 'LOCAL_URL') ? sprintf($token_explain, generate_board_url() . '/') : $token_explain,
));
}
return;
@@ -347,6 +347,9 @@ class acp_bbcodes
'LOCAL_URL' => array(
'!(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')!e' => "\$this->bbcode_specialchars('$1')"
),
'RELATIVE_URL' => array(
'!(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')!e' => "\$this->bbcode_specialchars('$1')"
),
'EMAIL' => array(
'!(' . get_preg_expression('email') . ')!ie' => "\$this->bbcode_specialchars('$1')"
),
@@ -373,6 +376,7 @@ class acp_bbcodes
$sp_tokens = array(
'URL' => '(?i)((?:' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('url')) . ')|(?:' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('www_url')) . '))(?-i)',
'LOCAL_URL' => '(?i)(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')(?-i)',
'RELATIVE_URL' => '(?i)(' . str_replace(array('!', '\#'), array('\!', '#'), get_preg_expression('relative_url')) . ')(?-i)',
'EMAIL' => '(' . get_preg_expression('email') . ')',
'TEXT' => '(.*?)',
'SIMPLETEXT' => '([a-zA-Z0-9-+.,_ ]+)',
@@ -429,7 +433,11 @@ class acp_bbcodes
$fp_replace = str_replace($token, $replace, $fp_replace);
$sp_match = str_replace(preg_quote($token, '!'), $sp_tokens[$token_type], $sp_match);
$sp_replace = str_replace($token, '${' . ($n + 1) . '}', $sp_replace);
// Prepend the board url to local relative links
$replace_prepend = ($token_type === 'LOCAL_URL') ? generate_board_url() . '/' : '';
$sp_replace = str_replace($token, $replace_prepend . '${' . ($n + 1) . '}', $sp_replace);
}
$fp_match = '!' . $fp_match . '!' . $modifiers;

View File

@@ -134,11 +134,11 @@ class bbcode
$style_resource_locator = new phpbb_style_resource_locator();
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path);
$template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);
$template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context(), $phpbb_extension_manager);
$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $template);
$style->set_style();
$template->set_filenames(array('bbcode.html' => 'bbcode.html'));
$this->template_filename = $style_resource_locator->get_source_file_for_handle('bbcode.html');
$this->template_filename = $template->get_source_file_for_handle('bbcode.html');
}
$bbcode_ids = $rowset = $sql = array();

View File

@@ -37,16 +37,24 @@ class phpbb_controller_resolver implements ControllerResolverInterface
*/
protected $container;
/**
* phpbb_style object
* @var phpbb_style
*/
protected $style;
/**
* Construct method
*
* @param phpbb_user $user User Object
* @param ContainerInterface $container ContainerInterface object
* @param phpbb_style $style
*/
public function __construct(phpbb_user $user, ContainerInterface $container)
public function __construct(phpbb_user $user, ContainerInterface $container, phpbb_style $style = null)
{
$this->user = $user;
$this->container = $container;
$this->style = $style;
}
/**
@@ -80,6 +88,24 @@ class phpbb_controller_resolver implements ControllerResolverInterface
$controller_object = $this->container->get($service);
/*
* If this is an extension controller, we'll try to automatically set
* the style paths for the extension (the ext author can change them
* if necessary).
*/
$controller_dir = explode('_', get_class($controller_object));
// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ...
if (!is_null($this->style) && isset($controller_dir[3]) && $controller_dir[1] === 'ext')
{
$controller_style_dir = 'ext/' . $controller_dir[2] . '/' . $controller_dir[3] . '/styles';
if (is_dir($controller_style_dir))
{
$this->style->set_style(array($controller_style_dir, 'styles'));
}
}
return array($controller_object, $method);
}

View File

@@ -0,0 +1,57 @@
<?php
/**
*
* @package migration
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_db_migration_data_30x_local_url_bbcode extends phpbb_db_migration
{
static public function depends_on()
{
return array('phpbb_db_migration_data_30x_3_0_12_rc1');
}
public function update_data()
{
return array(
array('custom', array(array($this, 'update_local_url_bbcode'))),
);
}
/**
* Update BBCodes that currently use the LOCAL_URL tag
*
* To fix http://tracker.phpbb.com/browse/PHPBB3-8319 we changed
* the second_pass_replace value, so that needs updating for existing ones
*/
public function update_local_url_bbcode()
{
$sql = 'SELECT *
FROM ' . BBCODES_TABLE . '
WHERE bbcode_match ' . $this->db->sql_like_expression($this->db->any_char . 'LOCAL_URL' . $this->db->any_char);
$result = $this->db->sql_query($sql);
while ($row = $this->db->sql_fetchrow($result))
{
if (!class_exists('acp_bbcodes'))
{
global $phpEx;
phpbb_require_updated('includes/acp/acp_bbcodes.' . $phpEx);
}
$bbcode_match = $row['bbcode_match'];
$bbcode_tpl = $row['bbcode_tpl'];
$acp_bbcodes = new acp_bbcodes();
$sql_ary = $acp_bbcodes->build_regexp($bbcode_match, $bbcode_tpl);
$sql = 'UPDATE ' . BBCODES_TABLE . '
SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . '
WHERE bbcode_id = ' . (int) $row['bbcode_id'];
$this->sql_query($sql);
}
$this->db->sql_freeresult($result);
}
}

View File

@@ -20,6 +20,67 @@ if (!defined('IN_PHPBB'))
exit;
}
/**
* Get DB connection from config.php.
*
* Used to bootstrap the container.
*
* @param string $config_file
* @return phpbb_db_driver
*/
function phpbb_bootstrap_db_connection($config_file)
{
require($config_file);
$dbal_driver_class = phpbb_convert_30_dbms_to_31($dbms);
return new $dbal_driver_class($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, defined('PHPBB_DB_NEW_LINK'));
}
/**
* Get table prefix from config.php.
*
* Used to bootstrap the container.
*
* @param string $config_file
* @return string table prefix
*/
function phpbb_bootstrap_table_prefix($config_file)
{
require($config_file);
return $table_prefix;
}
/**
* Get enabled extensions.
*
* Used to bootstrap the container.
*
* @param string $config_file
* @return array enabled extensions
*/
function phpbb_bootstrap_enabled_exts($config_file)
{
$db = phpbb_bootstrap_db_connection($config_file);
$table_prefix = phpbb_bootstrap_table_prefix($config_file);
$extension_table = $table_prefix.'ext';
$sql = 'SELECT *
FROM ' . $extension_table . '
WHERE ext_active = 1';
$result = $db->sql_query($sql);
$rows = $db->sql_fetchrowset($result);
$db->sql_freeresult($result);
$exts = array();
foreach ($rows as $row)
{
$exts[$row['ext_name']] = $phpbb_root_path . 'ext/' . $row['ext_name'] . '/';
}
return $exts;
}
/**
* Create the ContainerBuilder object
*
@@ -79,16 +140,9 @@ function phpbb_create_install_container($phpbb_root_path, $php_ext)
* @param string $php_ext PHP Extension
* @return ContainerBuilder object (compiled)
*/
function phpbb_create_compiled_container(array $extensions, array $passes, $phpbb_root_path, $php_ext)
function phpbb_create_compiled_container($config_file, array $extensions, array $passes, $phpbb_root_path, $php_ext)
{
// Create a temporary container for access to the ext.manager service
$tmp_container = phpbb_create_container($extensions, $phpbb_root_path, $php_ext);
$tmp_container->compile();
// XXX stop writing to global $cache when
// http://tracker.phpbb.com/browse/PHPBB3-11203 is fixed
$GLOBALS['cache'] = $tmp_container->get('cache');
$installed_exts = $tmp_container->get('ext.manager')->all_enabled();
$installed_exts = phpbb_bootstrap_enabled_exts($config_file);
// Now pass the enabled extension paths into the ext compiler extension
$extensions[] = new phpbb_di_extension_ext($installed_exts);
@@ -115,7 +169,7 @@ function phpbb_create_compiled_container(array $extensions, array $passes, $phpb
* @param string $php_ext PHP Extension
* @return ContainerBuilder object (compiled)
*/
function phpbb_create_dumped_container(array $extensions, array $passes, $phpbb_root_path, $php_ext)
function phpbb_create_dumped_container($config_file, array $extensions, array $passes, $phpbb_root_path, $php_ext)
{
// Check for our cached container; if it exists, use it
$container_filename = phpbb_container_filename($phpbb_root_path, $php_ext);
@@ -155,7 +209,7 @@ function phpbb_create_dumped_container(array $extensions, array $passes, $phpbb_
* @param string $php_ext PHP Extension
* @return ContainerBuilder object (compiled)
*/
function phpbb_create_dumped_container_unless_debug(array $extensions, array $passes, $phpbb_root_path, $php_ext)
function phpbb_create_dumped_container_unless_debug($config_file, array $extensions, array $passes, $phpbb_root_path, $php_ext)
{
$container_factory = defined('DEBUG') ? 'phpbb_create_compiled_container' : 'phpbb_create_dumped_container';
return $container_factory($extensions, $passes, $phpbb_root_path, $php_ext);
@@ -172,9 +226,11 @@ function phpbb_create_dumped_container_unless_debug(array $extensions, array $pa
*/
function phpbb_create_default_container($phpbb_root_path, $php_ext)
{
$config_file = $phpbb_root_path . 'config.' . $php_ext;
return phpbb_create_dumped_container_unless_debug(
$config_file,
array(
new phpbb_di_extension_config($phpbb_root_path . 'config.' . $php_ext),
new phpbb_di_extension_config($config_file),
new phpbb_di_extension_core($phpbb_root_path),
),
array(

View File

@@ -27,8 +27,9 @@ class messenger
var $mail_priority = MAIL_NORMAL_PRIORITY;
var $use_queue = true;
var $tpl_obj = NULL;
var $tpl_msg = array();
/** @var phpbb_template */
protected $template;
var $eol = "\n";
/**
@@ -55,10 +56,10 @@ class messenger
$this->vars = $this->msg = $this->replyto = $this->from = '';
$this->mail_priority = MAIL_NORMAL_PRIORITY;
}
/**
* Set addresses for to/im as available
*
*
* @param array $user User row
*/
function set_addresses($user)
@@ -210,6 +211,8 @@ class messenger
{
global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
$this->setup_template();
if (!trim($template_file))
{
trigger_error('No template file for emailing set.', E_USER_ERROR);
@@ -219,46 +222,43 @@ class messenger
{
// fall back to board default language if the user's language is
// missing $template_file. If this does not exist either,
// $tpl->set_filenames will do a trigger_error
// $this->template->set_filenames will do a trigger_error
$template_lang = basename($config['default_lang']);
}
// tpl_msg now holds a template object we can use to parse the template file
if (!isset($this->tpl_msg[$template_lang . $template_file]))
if ($template_path)
{
$style_resource_locator = new phpbb_style_resource_locator();
$style_path_provider = new phpbb_style_extension_path_provider($phpbb_extension_manager, new phpbb_style_path_provider(), $phpbb_root_path);
$tpl = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, new phpbb_template_context(), $phpbb_extension_manager);
$style = new phpbb_style($phpbb_root_path, $phpEx, $config, $user, $style_resource_locator, $style_path_provider, $tpl);
$template_paths = array(
$template_path,
);
}
else
{
$template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$template_path .= $template_lang . '/email';
$this->tpl_msg[$template_lang . $template_file] = $tpl;
$template_paths = array(
$template_path,
);
$fallback_template_path = false;
if (!$template_path)
// we can only specify default language fallback when the path is not a custom one for which we
// do not know the default language alternative
if ($template_lang !== basename($config['default_lang']))
{
$template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$template_path .= $template_lang . '/email';
$fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$fallback_template_path .= basename($config['default_lang']) . '/email';
// we can only specify default language fallback when the path is not a custom one for which we
// do not know the default language alternative
if ($template_lang !== basename($config['default_lang']))
{
$fallback_template_path = (!empty($user->lang_path)) ? $user->lang_path : $phpbb_root_path . 'language/';
$fallback_template_path .= basename($config['default_lang']) . '/email';
}
$template_paths[] = $fallback_template_path;
}
$style->set_custom_style($template_lang . '_email', array($template_path, $fallback_template_path), array(), '');
$tpl->set_filenames(array(
'body' => $template_file . '.txt',
));
}
$this->tpl_obj = &$this->tpl_msg[$template_lang . $template_file];
$this->vars = &$this->tpl_obj->_rootref;
$this->tpl_msg = '';
$this->set_template_paths($template_lang . '_email', $template_paths);
$this->template->set_filenames(array(
'body' => $template_file . '.txt',
));
$this->vars = $this->template->get_template_vars();
return true;
}
@@ -268,22 +268,16 @@ class messenger
*/
function assign_vars($vars)
{
if (!is_object($this->tpl_obj))
{
return;
}
$this->setup_template();
$this->tpl_obj->assign_vars($vars);
$this->template->assign_vars($vars);
}
function assign_block_vars($blockname, $vars)
{
if (!is_object($this->tpl_obj))
{
return;
}
$this->setup_template();
$this->tpl_obj->assign_block_vars($blockname, $vars);
$this->template->assign_block_vars($blockname, $vars);
}
/**
@@ -316,7 +310,7 @@ class messenger
}
// Parse message through template
$this->msg = trim($this->tpl_obj->assign_display('body'));
$this->msg = trim($this->template->assign_display('body'));
// Because we use \n for newlines in the body message we need to fix line encoding errors for those admins who uploaded email template files in the wrong encoding
$this->msg = str_replace("\r\n", "\n", $this->msg);
@@ -643,6 +637,31 @@ class messenger
unset($addresses);
return true;
}
/**
* Setup template engine
*/
protected function setup_template()
{
global $config, $phpbb_root_path, $phpEx, $user, $phpbb_extension_manager;
if ($this->template instanceof phpbb_template)
{
return;
}
$this->template = new phpbb_template_twig($phpbb_root_path, $phpEx, $config, $user, new phpbb_template_context(), $phpbb_extension_manager);
}
/**
* Set template paths to load
*/
protected function set_template_paths($path_name, $paths)
{
$this->setup_template();
$this->template->set_style_names(array($path_name), $paths);
}
}
/**

View File

@@ -455,7 +455,7 @@ class p_master
*/
function load_active($mode = false, $module_url = false, $execute_module = true)
{
global $phpbb_root_path, $phpbb_admin_path, $phpEx, $user;
global $phpbb_root_path, $phpbb_admin_path, $phpEx, $user, $phpbb_style;
$module_path = $this->include_path . $this->p_class;
$icat = request_var('icat', '');
@@ -494,6 +494,24 @@ class p_master
// We pre-define the action parameter we are using all over the place
if (defined('IN_ADMIN'))
{
/*
* If this is an extension module, we'll try to automatically set
* the style paths for the extension (the ext author can change them
* if necessary).
*/
$module_dir = explode('_', get_class($this->module));
// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ...
if (isset($module_dir[3]) && $module_dir[1] === 'ext')
{
$module_style_dir = $phpbb_root_path . 'ext/' . $module_dir[2] . '/' . $module_dir[3] . '/adm/style';
if (is_dir($module_style_dir))
{
$phpbb_style->set_custom_style('admin', array($module_style_dir, $phpbb_admin_path . 'style'), array(), '');
}
}
// 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'])
{
@@ -505,6 +523,24 @@ class p_master
}
else
{
/*
* If this is an extension module, we'll try to automatically set
* the style paths for the extension (the ext author can change them
* if necessary).
*/
$module_dir = explode('_', get_class($this->module));
// 0 phpbb, 1 ext, 2 vendor, 3 extension name, ...
if (isset($module_dir[3]) && $module_dir[1] === 'ext')
{
$module_style_dir = 'ext/' . $module_dir[2] . '/' . $module_dir[3] . '/styles';
if (is_dir($phpbb_root_path . $module_style_dir))
{
$phpbb_style->set_style(array($module_style_dir, 'styles'));
}
}
// If user specified the module url we will use it...
if ($module_url !== false)
{

View File

@@ -85,28 +85,62 @@ class phpbb_style
}
/**
* Set style location based on (current) user's chosen style.
* Get the style tree of the style preferred by the current user
*
* @return array Style tree, most specific first
*/
public function set_style()
public function get_user_style()
{
$style_path = $this->user->style['style_path'];
$style_dirs = ($this->user->style['style_parent_id']) ? array_reverse(explode('/', $this->user->style['style_parent_tree'])) : array();
$style_list = array(
$this->user->style['style_path'],
);
$names = array($style_path);
foreach ($style_dirs as $dir)
if ($this->user->style['style_parent_id'])
{
$names[] = $dir;
$style_list = array_merge($style_list, array_reverse(explode('/', $this->user->style['style_parent_tree'])));
}
// Add 'all' path, used as last fallback path by events and extensions
//$names[] = 'all';
return $style_list;
}
/**
* Set style location based on (current) user's chosen style.
*
* @param array $style_directories The directories to add style paths for
* E.g. array('ext/foo/bar/styles', 'styles')
* Default: array('styles') (phpBB's style directory)
* @return bool true
*/
public function set_style($style_directories = array('styles'))
{
$this->names = $this->get_user_style();
$paths = array();
foreach ($names as $name)
foreach ($style_directories as $directory)
{
$paths[] = $this->get_style_path($name);
foreach ($this->names as $name)
{
$path = $this->get_style_path($name, $directory);
if (is_dir($path))
{
$paths[] = $path;
}
}
}
return $this->set_custom_style($style_path, $paths, $names);
$this->provider->set_styles($paths);
$this->locator->set_paths($this->provider);
$new_paths = array();
foreach ($paths as $path)
{
$new_paths[] = $path . '/template/';
}
$this->template->set_style_names($this->names, $new_paths, ($style_directories === array('styles')));
return true;
}
/**
@@ -118,6 +152,7 @@ class phpbb_style
* @param array or string $paths Array of style paths, relative to current root directory
* @param array $names Array of names of templates in inheritance tree order, used by extensions. If empty, $name will be used.
* @param string $template_path Path to templates, relative to style directory. False if path should be set to default (templates/).
* @return bool true
*/
public function set_custom_style($name, $paths, $names = array(), $template_path = false)
{
@@ -135,18 +170,18 @@ class phpbb_style
$this->provider->set_styles($paths);
$this->locator->set_paths($this->provider);
$this->template->set_style_names($names);
if ($template_path !== false)
{
$this->locator->set_template_path($template_path);
}
else
$new_paths = array();
foreach ($paths as $path)
{
$this->locator->set_default_template_path();
$new_paths[] = $path . '/' . (($template_path !== false) ? $template_path : 'template/');
}
$this->template->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $name) . '_';
$this->template->set_style_names($names, $new_paths);
return true;
}
@@ -155,11 +190,14 @@ class phpbb_style
* Get location of style directory for specific style_path
*
* @param string $path Style path, such as "prosilver"
* @param string $style_base_directory The base directory the style is in
* E.g. 'styles', 'ext/foo/bar/styles'
* Default: 'styles'
* @return string Path to style directory, relative to current path
*/
public function get_style_path($path)
public function get_style_path($path, $style_base_directory = 'styles')
{
return $this->phpbb_root_path . 'styles/' . $path;
return $this->phpbb_root_path . trim($style_base_directory, '/') . '/' . $path;
}
/**

View File

@@ -1,166 +0,0 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2005 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
stream_filter_register('phpbb_template', 'phpbb_template_filter');
/**
* Extension of template class - Functions needed for compiling templates only.
*
* @package phpBB3
* @uses template_filter As a PHP stream filter to perform compilation of templates
*/
class phpbb_template_compile
{
/**
* Array of parameters to forward to template filter
*
* @var array
*/
private $filter_params;
/**
* Array of default parameters
*
* @var array
*/
private $default_filter_params;
/**
* Constructor.
*
* @param bool $allow_php Whether PHP code will be allowed in templates (inline PHP code, PHP tag and INCLUDEPHP tag)
* @param array $style_names Name of style to which the template being compiled belongs and parents in style tree order
* @param phpbb_style_resource_locator $locator Resource locator
* @param string $phpbb_root_path Path to phpBB root directory
* @param phpbb_extension_manager $extension_manager Extension manager to use for finding template fragments in extensions; if null, template events will not be invoked
* @param phpbb_user $user Current user
*/
public function __construct($allow_php, $style_names, $locator, $phpbb_root_path, $extension_manager = null, $user = null)
{
$this->filter_params = $this->default_filter_params = array(
'allow_php' => $allow_php,
'style_names' => $style_names,
'locator' => $locator,
'phpbb_root_path' => $phpbb_root_path,
'extension_manager' => $extension_manager,
'user' => $user,
'template_compile' => $this,
'cleanup' => true,
);
}
/**
* Set filter parameters
*
* @param array $params Array of parameters (will be merged onto $this->filter_params)
*/
public function set_filter_params($params)
{
$this->filter_params = array_merge(
$this->filter_params,
$params
);
}
/**
* Reset filter parameters to their default settings
*/
public function reset_filter_params()
{
$this->filter_params = $this->default_filter_params;
}
/**
* Compiles template in $source_file and writes compiled template to
* cache directory
*
* @param string $handle Template handle to compile
* @param string $source_file Source template file
* @return bool Return true on success otherwise false
*/
public function compile_file_to_file($source_file, $compiled_file)
{
$lock = new phpbb_lock_flock($compiled_file);
$lock->acquire();
$source_handle = @fopen($source_file, 'rb');
$destination_handle = @fopen($compiled_file, 'wb');
if (!$source_handle || !$destination_handle)
{
return false;
}
$this->compile_stream_to_stream($source_handle, $destination_handle);
@fclose($source_handle);
@fclose($destination_handle);
phpbb_chmod($compiled_file, CHMOD_READ | CHMOD_WRITE);
$lock->release();
clearstatcache();
return true;
}
/**
* Compiles a template located at $source_file.
*
* Returns PHP source suitable for eval().
*
* @param string $source_file Source template file
* @return string|bool Return compiled code on successful compilation otherwise false
*/
public function compile_file($source_file)
{
$source_handle = @fopen($source_file, 'rb');
$destination_handle = @fopen('php://temp' ,'r+b');
if (!$source_handle || !$destination_handle)
{
return false;
}
$this->compile_stream_to_stream($source_handle, $destination_handle);
@fclose($source_handle);
rewind($destination_handle);
$contents = stream_get_contents($destination_handle);
@fclose($dest_handle);
return $contents;
}
/**
* Compiles contents of $source_stream into $dest_stream.
*
* A stream filter is appended to $source_stream as part of the
* process.
*
* @param resource $source_stream Source stream
* @param resource $dest_stream Destination stream
* @return null
*/
private function compile_stream_to_stream($source_stream, $dest_stream)
{
stream_filter_append($source_stream, 'phpbb_template', null, $this->filter_params);
stream_copy_to_stream($source_stream, $dest_stream);
}
}

View File

@@ -138,7 +138,7 @@ class phpbb_template_context
}
$s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0;
$vararray['S_ROW_COUNT'] = $s_row_count;
$vararray['S_ROW_COUNT'] = $vararray['S_ROW_NUM'] = $s_row_count;
// Assign S_FIRST_ROW
if (!$s_row_count)
@@ -146,6 +146,9 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true;
}
// Assign S_BLOCK_NAME
$vararray['S_BLOCK_NAME'] = $blocks[$blockcount];
// Now the tricky part, we always assign S_LAST_ROW and remove the entry before
// This is much more clever than going through the complete template data on display (phew)
$vararray['S_LAST_ROW'] = true;
@@ -158,12 +161,18 @@ class phpbb_template_context
// We're adding a new iteration to this block with the given
// variable assignments.
$str[$blocks[$blockcount]][] = $vararray;
// Set S_NUM_ROWS
foreach ($str[$blocks[$blockcount]] as &$mod_block)
{
$mod_block['S_NUM_ROWS'] = sizeof($str[$blocks[$blockcount]]);
}
}
else
{
// Top-level block.
$s_row_count = (isset($this->tpldata[$blockname])) ? sizeof($this->tpldata[$blockname]) : 0;
$vararray['S_ROW_COUNT'] = $s_row_count;
$vararray['S_ROW_COUNT'] = $vararray['S_ROW_NUM'] = $s_row_count;
// Assign S_FIRST_ROW
if (!$s_row_count)
@@ -171,6 +180,9 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true;
}
// Assign S_BLOCK_NAME
$vararray['S_BLOCK_NAME'] = $blockname;
// We always assign S_LAST_ROW and remove the entry before
$vararray['S_LAST_ROW'] = true;
if ($s_row_count > 0)
@@ -180,6 +192,12 @@ class phpbb_template_context
// Add a new iteration to this block with the variable assignments we were given.
$this->tpldata[$blockname][] = $vararray;
// Set S_NUM_ROWS
foreach ($this->tpldata[$blockname] as &$mod_block)
{
$mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]);
}
}
return true;
@@ -298,14 +316,26 @@ class phpbb_template_context
$vararray['S_FIRST_ROW'] = true;
}
// Assign S_BLOCK_NAME
$vararray['S_BLOCK_NAME'] = $blockname;
// Re-position template blocks
for ($i = sizeof($block); $i > $key; $i--)
{
$block[$i] = $block[$i-1];
$block[$i]['S_ROW_COUNT'] = $block[$i]['S_ROW_NUM'] = $i;
}
// Insert vararray at given position
$block[$key] = $vararray;
$block[$key]['S_ROW_COUNT'] = $block[$key]['S_ROW_NUM'] = $key;
// Set S_NUM_ROWS
foreach ($this->tpldata[$blockname] as &$mod_block)
{
$mod_block['S_NUM_ROWS'] = sizeof($this->tpldata[$blockname]);
}
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +0,0 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* Template renderer interface.
*
* Objects implementing this interface encapsulate a means of displaying
* a template.
*
* @package phpBB3
*/
interface phpbb_template_renderer
{
/**
* Displays the template managed by this renderer.
*
* @param phpbb_template_context $context Template context to use
* @param array $lang Language entries to use
*/
public function render($context, $lang);
}

View File

@@ -1,60 +0,0 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* Template renderer that stores compiled template's php code and
* displays it via eval.
*
* @package phpBB3
*/
class phpbb_template_renderer_eval implements phpbb_template_renderer
{
/**
* Template code to be eval'ed.
*/
private $code;
/**
* Constructor. Stores provided code for future evaluation.
* Template includes are delegated to template object $template.
*
* @param string $code php code of the template
* @param phpbb_template $template template object
*/
public function __construct($code, $template)
{
$this->code = $code;
$this->template = $template;
}
/**
* Displays the template managed by this renderer by eval'ing php code
* of the template.
*
* @param phpbb_template_context $context Template context to use
* @param array $lang Language entries to use
*/
public function render($context, $lang)
{
$_template = $this->template;
$_tpldata = &$context->get_data_ref();
$_rootref = &$context->get_root_ref();
$_lang = $lang;
eval(' ?>' . $this->code . '<?php ');
}
}

View File

@@ -1,60 +0,0 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2011 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* Template renderer that stores path to php file with template code
* and displays it by including the file.
*
* @package phpBB3
*/
class phpbb_template_renderer_include implements phpbb_template_renderer
{
/**
* Template path to be included.
*/
private $path;
/**
* Constructor. Stores path to the template for future inclusion.
* Template includes are delegated to template object $template.
*
* @param string $path path to the template
*/
public function __construct($path, $template)
{
$this->path = $path;
$this->template = $template;
}
/**
* Displays the template managed by this renderer by including
* the php file containing the template.
*
* @param phpbb_template_context $context Template context to use
* @param array $lang Language entries to use
*/
public function render($context, $lang)
{
$_template = $this->template;
$_tpldata = &$context->get_data_ref();
$_rootref = &$context->get_root_ref();
$_lang = $lang;
include($this->path);
}
}

View File

@@ -2,7 +2,7 @@
/**
*
* @package phpBB3
* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
@@ -15,143 +15,48 @@ if (!defined('IN_PHPBB'))
exit;
}
/**
* @todo
* IMG_ for image substitution?
* {IMG_[key]:[alt]:[type]}
* {IMG_ICON_CONTACT:CONTACT:full} -> $user->img('icon_contact', 'CONTACT', 'full');
*
* More in-depth...
* yadayada
*/
/**
* Base Template class.
* @package phpBB3
*/
class phpbb_template
interface phpbb_template
{
/**
* Template context.
* Stores template data used during template rendering.
* @var phpbb_template_context
*/
private $context;
/**
* Path of the cache directory for the template
* @var string
*/
public $cachepath = '';
/**
* phpBB root path
* @var string
*/
private $phpbb_root_path;
/**
* PHP file extension
* @var string
*/
private $php_ext;
/**
* phpBB config instance
* @var phpbb_config
*/
private $config;
/**
* Current user
* @var phpbb_user
*/
private $user;
/**
* Template locator
* @var phpbb_template_locator
*/
private $locator;
/**
* Extension manager.
* Clear the cache
*
* @var phpbb_extension_manager
* @return phpbb_template
*/
private $extension_manager;
/**
* Name of the style that the template being compiled and/or rendered
* belongs to, and its parents, in inheritance tree order.
*
* Used to invoke style-specific template events.
*
* @var array
*/
private $style_names;
/**
* Constructor.
*
* @param string $phpbb_root_path phpBB root path
* @param user $user current user
* @param phpbb_template_locator $locator template locator
* @param phpbb_template_context $context template context
* @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked
*/
public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_locator $locator, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$this->config = $config;
$this->user = $user;
$this->locator = $locator;
$this->context = $context;
$this->extension_manager = $extension_manager;
}
public function clear_cache();
/**
* Sets the template filenames for handles.
*
* @param array $filename_array Should be a hash of handle => filename pairs.
* @return phpbb_template $this
*/
public function set_filenames(array $filename_array)
{
$this->locator->set_filenames($filename_array);
return true;
}
public function set_filenames(array $filename_array);
/**
* Sets the style names corresponding to style hierarchy being compiled
* Sets the style names/paths corresponding to style hierarchy being compiled
* and/or rendered.
*
* @param array $style_names List of style names in inheritance tree order
* @return null
* @param array $style_paths List of style paths in inheritance tree order
* @return phpbb_template $this
*/
public function set_style_names(array $style_names)
{
$this->style_names = $style_names;
}
public function set_style_names(array $style_names, array $style_paths);
/**
* Clears all variables and blocks assigned to this template.
*
* @return phpbb_template $this
*/
public function destroy()
{
$this->context->clear();
}
public function destroy();
/**
* Reset/empty complete block
*
* @param string $blockname Name of block to destroy
* @return phpbb_template $this
*/
public function destroy_block_vars($blockname)
{
$this->context->destroy_block_vars($blockname);
}
public function destroy_block_vars($blockname);
/**
* Display a template for provided handle.
@@ -161,81 +66,9 @@ class phpbb_template
* This function calls hooks.
*
* @param string $handle Handle to display
* @return bool True on success, false on failure
* @return phpbb_template $this
*/
public function display($handle)
{
$result = $this->call_hook($handle, __FUNCTION__);
if ($result !== false)
{
return $result[0];
}
return $this->load_and_render($handle);
}
/**
* Loads a template for $handle, compiling it if necessary, and
* renders the template.
*
* @param string $handle Template handle to render
* @return bool True on success, false on failure
*/
private function load_and_render($handle)
{
$renderer = $this->_tpl_load($handle);
if ($renderer)
{
$renderer->render($this->context, $this->get_lang());
return true;
}
else
{
return false;
}
}
/**
* Calls hook if any is defined.
*
* @param string $handle Template handle being displayed.
* @param string $method Method name of the caller.
*/
private function call_hook($handle, $method)
{
global $phpbb_hook;
if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this))
{
if ($phpbb_hook->hook_return(array(__CLASS__, $method)))
{
$result = $phpbb_hook->hook_return_result(array(__CLASS__, $method));
return array($result);
}
}
return false;
}
/**
* Obtains language array.
* This is either lang property of $user property, or if
* it is not set an empty array.
* @return array language entries
*/
public function get_lang()
{
if (isset($this->user->lang))
{
$lang = $this->user->lang;
}
else
{
$lang = array();
}
return $lang;
}
public function display($handle);
/**
* Display the handle and assign the output to a template variable
@@ -244,118 +77,17 @@ class phpbb_template
* @param string $handle Handle to operate on
* @param string $template_var Template variable to assign compiled handle to
* @param bool $return_content If true return compiled handle, otherwise assign to $template_var
* @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true
* @return phpbb_template|string if $return_content is true return string of the compiled handle, otherwise return $this
*/
public function assign_display($handle, $template_var = '', $return_content = true)
{
ob_start();
$result = $this->display($handle);
$contents = ob_get_clean();
if ($result === false)
{
return false;
}
if ($return_content)
{
return $contents;
}
$this->assign_var($template_var, $contents);
return true;
}
/**
* Obtains a template renderer for a template identified by specified
* handle. The template renderer can display the template later.
*
* Template source will first be compiled into php code.
* If template cache is writable the compiled php code will be stored
* on filesystem and template will not be subsequently recompiled.
* If template cache is not writable template source will be recompiled
* every time it is needed. DEBUG define and load_tplcompile
* configuration setting may be used to force templates to be always
* recompiled.
*
* Returns an object implementing phpbb_template_renderer, or null
* if template loading or compilation failed. Call render() on the
* renderer to display the template. This will result in template
* contents sent to the output stream (unless, of course, output
* buffering is in effect).
*
* @param string $handle Handle of the template to load
* @return phpbb_template_renderer Template renderer object, or null on failure
* @uses phpbb_template_compile is used to compile template source
*/
private function _tpl_load($handle)
{
$output_file = $this->_compiled_file_for_handle($handle);
$recompile = defined('DEBUG') ||
!file_exists($output_file) ||
@filesize($output_file) === 0;
if ($recompile || $this->config['load_tplcompile'])
{
// Set only if a recompile or an mtime check are required.
$source_file = $this->locator->get_source_file_for_handle($handle);
if (!$recompile && @filemtime($output_file) < @filemtime($source_file))
{
$recompile = true;
}
}
// Recompile page if the original template is newer, otherwise load the compiled version
if (!$recompile)
{
return new phpbb_template_renderer_include($output_file, $this);
}
$compile = new phpbb_template_compile($this->config['tpl_allow_php'], $this->style_names, $this->locator, $this->phpbb_root_path, $this->extension_manager, $this->user);
if ($compile->compile_file_to_file($source_file, $output_file) !== false)
{
$renderer = new phpbb_template_renderer_include($output_file, $this);
}
else if (($code = $compile->compile_file($source_file)) !== false)
{
$renderer = new phpbb_template_renderer_eval($code, $this);
}
else
{
$renderer = null;
}
return $renderer;
}
/**
* Determines compiled file path for handle $handle.
*
* @param string $handle Template handle (i.e. "friendly" template name)
* @return string Compiled file path
*/
private function _compiled_file_for_handle($handle)
{
$source_file = $this->locator->get_filename_for_handle($handle);
$compiled_file = $this->cachepath . str_replace('/', '.', $source_file) . '.' . $this->php_ext;
return $compiled_file;
}
public function assign_display($handle, $template_var = '', $return_content = true);
/**
* Assign key variable pairs from an array
*
* @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/
public function assign_vars(array $vararray)
{
foreach ($vararray as $key => $val)
{
$this->assign_var($key, $val);
}
}
public function assign_vars(array $vararray);
/**
* Assign a single scalar value to a single key.
@@ -364,11 +96,9 @@ class phpbb_template
*
* @param string $varname Variable name
* @param string $varval Value to assign to variable
* @return phpbb_template $this
*/
public function assign_var($varname, $varval)
{
$this->context->assign_var($varname, $varval);
}
public function assign_var($varname, $varval);
/**
* Append text to the string value stored in a key.
@@ -377,24 +107,18 @@ class phpbb_template
*
* @param string $varname Variable name
* @param string $varval Value to append to variable
* @return phpbb_template $this
*/
public function append_var($varname, $varval)
{
$this->context->append_var($varname, $varval);
}
public function append_var($varname, $varval);
// Docstring is copied from phpbb_template_context method with the same name.
/**
* Assign key variable pairs from an array to a specified block
* @param string $blockname Name of block to assign $vararray to
* @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/
public function assign_block_vars($blockname, array $vararray)
{
return $this->context->assign_block_vars($blockname, $vararray);
}
public function assign_block_vars($blockname, array $vararray);
// Docstring is copied from phpbb_template_context method with the same name.
/**
* Change already assigned key variable pair (one-dimensional - single loop entry)
*
@@ -422,94 +146,12 @@ class phpbb_template
*
* @return bool false on error, true on success
*/
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert')
{
return $this->context->alter_block_array($blockname, $vararray, $key, $mode);
}
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert');
/**
* Include a separate template.
* Get path to template for handle (required for BBCode parser)
*
* This function is marked public due to the way the template
* implementation uses it. It is actually an implementation function
* and should not be considered part of template class's public API.
*
* @param string $filename Template filename to include
* @param bool $include True to include the file, false to just load it
* @uses template_compile is used to compile uncached templates
* @return string
*/
public function _tpl_include($filename, $include = true)
{
$this->locator->set_filenames(array($filename => $filename));
if (!$this->load_and_render($filename))
{
// trigger_error cannot be used here, as the output already started
echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n";
}
}
/**
* Include a PHP file.
*
* If a relative path is passed in $filename, it is considered to be
* relative to board root ($phpbb_root_path). Absolute paths are
* also allowed.
*
* This function is marked public due to the way the template
* implementation uses it. It is actually an implementation function
* and should not be considered part of template class's public API.
*
* @param string $filename Path to PHP file to include
*/
public function _php_include($filename)
{
if (phpbb_is_absolute($filename))
{
$file = $filename;
}
else
{
$file = $this->phpbb_root_path . $filename;
}
if (!file_exists($file))
{
// trigger_error cannot be used here, as the output already started
echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n";
return;
}
include($file);
}
/**
* Include JS file
*
* @param string $file file name
* @param bool $locate True if file needs to be located
* @param bool $relative True if path is relative to phpBB root directory. Ignored if $locate == true
*/
public function _js_include($file, $locate = false, $relative = false)
{
// Locate file
if ($locate)
{
$located = $this->locator->get_first_file_location(array($file), false, true);
if ($located)
{
$file = $located;
}
}
else if ($relative)
{
$file = $this->phpbb_root_path . $file;
}
$file .= (strpos($file, '?') === false) ? '?' : '&';
$file .= 'assets_version=' . $this->config['assets_version'];
// Add HTML code
$code = '<script src="' . htmlspecialchars($file) . '"></script>';
$this->context->append_var('SCRIPTS', $code);
}
public function get_source_file_for_handle($handle);
}

View File

@@ -0,0 +1,69 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* This class holds all DEFINE variables from the current page load
*/
class phpbb_template_twig_definition
{
/** @var array **/
protected $definitions = array();
/**
* Get a DEFINE'd variable
*
* @param string $name
* @return mixed Null if not found
*/
public function __call($name, $arguments)
{
return (isset($this->definitions[$name])) ? $this->definitions[$name] : null;
}
/**
* DEFINE a variable
*
* @param string $name
* @param mixed $value
* @return phpbb_template_twig_definition
*/
public function set($name, $value)
{
$this->definitions[$name] = $value;
return $this;
}
/**
* Append to a variable
*
* @param string $name
* @param string $value
* @return phpbb_template_twig_definition
*/
public function append($name, $value)
{
if (!isset($this->definitions[$name]))
{
$this->definitions[$name] = '';
}
$this->definitions[$name] .= $value;
return $this;
}
}

View File

@@ -0,0 +1,140 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_environment extends Twig_Environment
{
/** @var array */
protected $phpbb_extensions;
/** @var phpbb_config */
protected $phpbb_config;
/** @var string */
protected $phpbb_root_path;
/** @var array **/
protected $namespace_look_up_order = array('__main__');
/**
* Constructor
*
* @param phpbb_config $phpbb_config
* @param array $phpbb_extensions Array of enabled extensions (name => path)
* @param string $phpbb_root_path
* @param Twig_LoaderInterface $loader
* @param array $options Array of options to pass to Twig
*/
public function __construct($phpbb_config, $phpbb_extensions, $phpbb_root_path, Twig_LoaderInterface $loader = null, $options = array())
{
$this->phpbb_config = $phpbb_config;
$this->phpbb_extensions = $phpbb_extensions;
$this->phpbb_root_path = $phpbb_root_path;
return parent::__construct($loader, $options);
}
/**
* Get the list of enabled phpBB extensions
*
* Used in EVENT node
*
* @return array
*/
public function get_phpbb_extensions()
{
return $this->phpbb_extensions;
}
/**
* Get phpBB config
*
* @return phpbb_config
*/
public function get_phpbb_config()
{
return $this->phpbb_config;
}
/**
* Get the phpBB root path
*
* @return string
*/
public function get_phpbb_root_path()
{
return $this->phpbb_root_path;
}
/**
* Get the namespace look up order
*
* @return array
*/
public function getNamespaceLookUpOrder()
{
return $this->namespace_look_up_order;
}
/**
* Set the namespace look up order to load templates from
*
* @param array $namespace
* @return Twig_Environment
*/
public function setNamespaceLookUpOrder($namespace)
{
$this->namespace_look_up_order = $namespace;
return $this;
}
/**
* Loads a template by name.
*
* @param string $name The template name
* @param integer $index The index if it is an embedded template
* @return Twig_TemplateInterface A template instance representing the given template name
*/
public function loadTemplate($name, $index = null)
{
if (strpos($name, '@') === false)
{
foreach ($this->getNamespaceLookUpOrder() as $namespace)
{
try
{
if ($namespace === '__main__')
{
return parent::loadTemplate($name, $index);
}
return parent::loadTemplate('@' . $namespace . '/' . $name, $index);
}
catch (Twig_Error_Loader $e)
{
}
}
// We were unable to load any templates
throw $e;
}
else
{
return parent::loadTemplate($name, $index);
}
}
}

View File

@@ -0,0 +1,188 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_extension extends Twig_Extension
{
/** @var phpbb_template_context */
protected $context;
/** @var phpbb_user */
protected $user;
/**
* Constructor
*
* @param phpbb_template_context $context
* @param phpbb_user $user
* @return phpbb_template_twig_extension
*/
public function __construct(phpbb_template_context $context, $user)
{
$this->context = $context;
$this->user = $user;
}
/**
* Get the name of this extension
*
* @return string
*/
public function getName()
{
return 'phpbb';
}
/**
* Returns the token parser instance to add to the existing list.
*
* @return array An array of Twig_TokenParser instances
*/
public function getTokenParsers()
{
return array(
new phpbb_template_twig_tokenparser_define,
new phpbb_template_twig_tokenparser_include,
new phpbb_template_twig_tokenparser_includejs,
new phpbb_template_twig_tokenparser_includecss,
new phpbb_template_twig_tokenparser_event,
new phpbb_template_twig_tokenparser_includephp,
new phpbb_template_twig_tokenparser_php,
);
}
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
public function getFilters()
{
return array(
new Twig_SimpleFilter('subset', array($this, 'loop_subset'), array('needs_environment' => true)),
new Twig_SimpleFilter('addslashes', 'addslashes'),
);
}
/**
* Returns a list of global functions to add to the existing list.
*
* @return array An array of global functions
*/
public function getFunctions()
{
return array(
new Twig_SimpleFunction('lang', array($this, 'lang')),
);
}
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
public function getOperators()
{
return array(
array(
'!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
),
array(
// precedence settings are copied from similar operators in Twig core extension
'||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'eq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'ne' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'neq' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'<>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'===' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_equalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'!==' => array('precedence' => 20, 'class' => 'phpbb_template_twig_node_expression_binary_notequalequal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'gt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'gte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'ge' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'lt' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'lte' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'le' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'mod' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
),
);
}
/**
* Grabs a subset of a loop
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $item A variable
* @param integer $start Start of the subset
* @param integer $end End of the subset
* @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array)
*
* @return mixed The sliced variable
*/
function loop_subset(Twig_Environment $env, $item, $start, $end = null, $preserveKeys = false)
{
// We do almost the same thing as Twig's slice (array_slice), except when $end is positive
if ($end >= 1)
{
// When end is > 1, subset will end on the last item in an array with the specified $end
// This is different from slice in that it is the number we end on rather than the number
// of items to grab (length)
// Start must always be the actual starting number for this calculation (not negative)
$start = ($start < 0) ? sizeof($item) + $start : $start;
$end = $end - $start;
}
// We always include the last element (this was the past design)
$end = ($end == -1 || $end === null) ? null : $end + 1;
return twig_slice($env, $item, $start, $end, $preserveKeys);
}
/**
* Get output for a language variable (L_FOO, LA_FOO)
*
* This function checks to see if the language var was outputted to $context
* (e.g. in the ACP, L_TITLE)
* If not, we return the result of $user->lang()
*
* @param string $lang name
* @return string
*/
function lang()
{
$args = func_get_args();
$key = $args[0];
$context = $this->context->get_data_ref();
$context_vars = $context['.'][0];
if (isset($context_vars['L_' . $key]))
{
return $context_vars['L_' . $key];
}
// LA_ is transformed into lang(\'$1\')|addslashes, so we should not
// need to check for it
return call_user_func_array(array($this->user, 'lang'), $args);
}
}

View File

@@ -0,0 +1,300 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_lexer extends Twig_Lexer
{
public function tokenize($code, $filename = null)
{
// Our phpBB tags
// Commented out tokens are handled separately from the main replace
$phpbb_tags = array(
/*'BEGIN',
'BEGINELSE',
'END',
'IF',
'ELSE',
'ELSEIF',
'ENDIF',
'DEFINE',
'UNDEFINE',*/
'ENDDEFINE',
'INCLUDE',
'INCLUDEPHP',
'INCLUDEJS',
'INCLUDECSS',
'PHP',
'ENDPHP',
'EVENT',
);
// Twig tag masks
$twig_tags = array(
'autoescape',
'endautoescape',
'if',
'elseif',
'else',
'endif',
'block',
'endblock',
'use',
'extends',
'embed',
'filter',
'endfilter',
'flush',
'for',
'endfor',
'macro',
'endmacro',
'import',
'from',
'sandbox',
'endsandbox',
'set',
'endset',
'spaceless',
'endspaceless',
'verbatim',
'endverbatim',
);
// Fix tokens that may have inline variables (e.g. <!-- DEFINE $TEST = '{FOO}')
$code = $this->fix_inline_variable_tokens(array(
'DEFINE.+=',
'INCLUDE',
'INCLUDEPHP',
'INCLUDEJS',
'INCLUDECSS',
), $code);
// Fix our BEGIN statements
$code = $this->fix_begin_tokens($code);
// Fix our IF tokens
$code = $this->fix_if_tokens($code);
// Fix our DEFINE tokens
$code = $this->fix_define_tokens($code);
// Replace all of our starting tokens, <!-- TOKEN --> with Twig style, {% TOKEN %}
// This also strips outer parenthesis, <!-- IF (blah) --> becomes <!-- IF blah -->
$code = preg_replace('#<!-- (' . implode('|', $phpbb_tags) . ')(?: (.*?) ?)?-->#', '{% $1 $2 %}', $code);
// Replace all of our twig masks with Twig code (e.g. <!-- BLOCK .+ --> with {% block $1 %})
$code = $this->replace_twig_tag_masks($code, $twig_tags);
// Replace all of our language variables, {L_VARNAME}, with Twig style, {{ lang('NAME') }}
// Appends any filters after lang()
$code = preg_replace('#{L_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2 }}', $code);
// Replace all of our escaped language variables, {LA_VARNAME}, with Twig style, {{ lang('NAME')|addslashes }}
// Appends any filters after lang(), but before addslashes
$code = preg_replace('#{LA_([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ lang(\'$1\')$2|addslashes }}', $code);
// Replace all of our variables, {VARNAME}, with Twig style, {{ VARNAME }}
// Appends any filters
$code = preg_replace('#{([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ $1$2 }}', $code);
return parent::tokenize($code, $filename);
}
/**
* Fix tokens that may have inline variables
*
* E.g. <!-- INCLUDE {TEST}.html
*
* @param array $tokens array of tokens to search for (imploded to a regular expression)
* @param string $code
* @return string
*/
protected function fix_inline_variable_tokens($tokens, $code)
{
$callback = function($matches)
{
// Remove any quotes that may have been used in different implementations
// E.g. DEFINE $TEST = 'blah' vs INCLUDE foo
// Replace {} with start/end to parse variables (' ~ TEST ~ '.html)
$matches[2] = str_replace(array('"', "'", '{', '}'), array('', '', "' ~ ", " ~ '"), $matches[2]);
// Surround the matches in single quotes ('' ~ TEST ~ '.html')
return "<!-- {$matches[1]} '{$matches[2]}' -->";
};
return preg_replace_callback('#<!-- (' . implode('|', $tokens) . ') (.+?) -->#', $callback, $code);
}
/**
* Fix begin tokens (convert our BEGIN to Twig for)
*
* Not meant to be used outside of this context, public because the anonymous function calls this
*
* @param string $code
* @param array $parent_nodes (used in recursion)
* @return string
*/
public function fix_begin_tokens($code, $parent_nodes = array())
{
// PHP 5.3 cannot use $this in an anonymous function, so use this as a work-around
$parent_class = $this;
$callback = function ($matches) use ($parent_class, $parent_nodes)
{
$name = $matches[1];
$subset = trim(substr($matches[2], 1, -1)); // Remove parenthesis
$body = $matches[3];
// Is the designer wanting to call another loop in a loop?
// <!-- BEGIN loop -->
// <!-- BEGIN !loop2 -->
// <!-- END !loop2 -->
// <!-- END loop -->
// 'loop2' is actually on the same nesting level as 'loop' you assign
// variables to it with template->assign_block_vars('loop2', array(...))
if (strpos($name, '!') === 0)
{
// Count the number if ! occurrences
$count = substr_count($name, '!');
for ($i = 0; $i < $count; $i++)
{
array_pop($parent_nodes);
$name = substr($name, 1);
}
}
// Remove all parent nodes, e.g. foo, bar from foo.bar.foobar.VAR
foreach ($parent_nodes as $node)
{
$body = preg_replace('#([^a-zA-Z0-9_])' . $node . '\.([a-zA-Z0-9_]+)\.#', '$1$2.', $body);
}
// Add current node to list of parent nodes for child nodes
$parent_nodes[] = $name;
// Recursive...fix any child nodes
$body = $parent_class->fix_begin_tokens($body, $parent_nodes);
// Rename loopname vars (to prevent collisions, loop children are named (loop name)_loop_element)
$body = str_replace($name . '.', $name . '_loop_element.', $body);
// Need the parent variable name
array_pop($parent_nodes);
$parent = (!empty($parent_nodes)) ? end($parent_nodes) . '_loop_element.' : '';
if ($subset !== '')
{
$subset = '|subset(' . $subset . ')';
}
// Turn into a Twig for loop, using (loop name)_loop_element for each child
return "{% for {$name}_loop_element in {$parent}{$name}{$subset} %}{$body}{% endfor %}";
};
// Replace <!-- BEGINELSE --> correctly, only needs to be done once
$code = str_replace('<!-- BEGINELSE -->', '{% else %}', $code);
return preg_replace_callback('#<!-- BEGIN ([!a-zA-Z0-9_]+)(\([0-9,\-]+\))? -->(.+?)<!-- END \1 -->#s', $callback, $code);
}
/**
* Fix IF statements
*
* @param string $code
* @return string
*/
protected function fix_if_tokens($code)
{
$callback = function($matches)
{
// Replace $TEST with definition.TEST
$matches[1] = preg_replace('#\s\$([a-zA-Z_0-9]+)#', ' definition.$1', $matches[1]);
// Replace .test with test|length
$matches[1] = preg_replace('#\s\.([a-zA-Z_0-9\.]+)#', ' $1|length', $matches[1]);
return '<!-- IF' . $matches[1] . '-->';
};
// Replace our "div by" with Twig's divisibleby (Twig does not like test names with spaces)
$code = preg_replace('# div by ([0-9]+)#', ' divisibleby($1)', $code);
return preg_replace_callback('#<!-- IF((.*)[\s][\$|\.|!]([^\s]+)(.*))-->#', $callback, $code);
}
/**
* Fix DEFINE statements and {$VARNAME} variables
*
* @param string $code
* @return string
*/
protected function fix_define_tokens($code)
{
/**
* Changing $VARNAME to definition.varname because set is only local
* context (e.g. DEFINE $TEST will only make $TEST available in current
* template and any child templates, but not any parent templates).
*
* DEFINE handles setting it properly to definition in its node, but the
* variables reading FROM it need to be altered to definition.VARNAME
*
* Setting up definition as a class in the array passed to Twig
* ($context) makes set definition.TEST available in the global context
*/
// Replace <!-- DEFINE $NAME with {% DEFINE definition.NAME
$code = preg_replace('#<!-- DEFINE \$(.*)-->#', '{% DEFINE $1 %}', $code);
// Changing UNDEFINE NAME to DEFINE NAME = null to save from creating an extra token parser/node
$code = preg_replace('#<!-- UNDEFINE \$(.*)-->#', '{% DEFINE $1= null %}', $code);
// Replace all of our variables, {$VARNAME}, with Twig style, {{ definition.VARNAME }}
$code = preg_replace('#{\$([a-zA-Z0-9_\.]+)}#', '{{ definition.$1 }}', $code);
// Replace all of our variables, ~ $VARNAME ~, with Twig style, ~ definition.VARNAME ~
$code = preg_replace('#~ \$([a-zA-Z0-9_\.]+) ~#', '~ definition.$1 ~', $code);
return $code;
}
/**
* Replace Twig tag masks with Twig tag calls
*
* E.g. <!-- BLOCK foo --> with {% block foo %}
*
* @param string $code
* @param array $twig_tags All tags we want to create a mask for
* @return string
*/
protected function replace_twig_tag_masks($code, $twig_tags)
{
$callback = function ($matches)
{
$matches[1] = strtolower($matches[1]);
return "{% {$matches[1]}{$matches[2]}%}";
};
foreach ($twig_tags as &$tag)
{
$tag = strtoupper($tag);
}
// twig_tags is an array of the twig tags, which are all lowercase, but we use all uppercase tags
$code = preg_replace_callback('#<!-- (' . implode('|', $twig_tags) . ')(.*?)-->#',$callback, $code);
return $code;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_define extends Twig_Node
{
public function __construct($capture, Twig_NodeInterface $name, Twig_NodeInterface $value, $lineno, $tag = null)
{
parent::__construct(array('name' => $name, 'value' => $value), array('capture' => $capture, 'safe' => false), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
if ($this->getAttribute('capture')) {
$compiler
->write("ob_start();\n")
->subcompile($this->getNode('value'))
;
$compiler->write("\$value = ('' === \$value = ob_get_clean()) ? '' : new Twig_Markup(\$value, \$this->env->getCharset());\n");
}
else
{
$compiler
->write("\$value = ")
->subcompile($this->getNode('value'))
->raw(";\n")
;
}
$compiler
->write("\$context['definition']->set('")
->raw($this->getNode('name')->getAttribute('name'))
->raw("', \$value);\n")
;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_event extends Twig_Node
{
/** @var Twig_Environment */
protected $environment;
public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null)
{
$this->environment = $environment;
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$location = $this->getNode('expr')->getAttribute('name');
foreach ($this->environment->get_phpbb_extensions() as $ext_namespace => $ext_path)
{
$ext_namespace = str_replace('/', '_', $ext_namespace);
if (defined('DEBUG'))
{
// If debug mode is enabled, lets check for new/removed EVENT
// templates on page load rather than at compile. This is
// slower, but makes developing extensions easier (no need to
// purge the cache when a new event template file is added)
$compiler
->write("if (\$this->env->getLoader()->exists('@{$ext_namespace}/{$location}.html')) {\n")
->indent()
;
}
if (defined('DEBUG') || $this->environment->getLoader()->exists('@' . $ext_namespace . '/' . $location . '.html'))
{
$compiler
->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
// We set the namespace lookup order to be this extension first, then the main path
->write("\$this->env->setNamespaceLookUpOrder(array('{$ext_namespace}', '__main__'));\n")
->write("\$this->env->loadTemplate('@{$ext_namespace}/{$location}.html')->display(\$context);\n")
->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n")
;
}
if (defined('DEBUG'))
{
$compiler
->outdent()
->write("}\n\n")
;
}
}
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_expression_binary_equalequal extends Twig_Node_Expression_Binary
{
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('===');
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_expression_binary_notequalequal extends Twig_Node_Expression_Binary
{
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('!==');
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_include extends Twig_Node_Include
{
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler
->write("\$location = ")
->subcompile($this->getNode('expr'))
->raw(";\n")
->write("\$namespace = false;\n")
->write("if (strpos(\$location, '@') === 0) {\n")
->indent()
->write("\$namespace = substr(\$location, 1, strpos(\$location, '/') - 1);\n")
->write("\$previous_look_up_order = \$this->env->getNamespaceLookUpOrder();\n")
// We set the namespace lookup order to be this namespace first, then the main path
->write("\$this->env->setNamespaceLookUpOrder(array(\$namespace, '__main__'));\n")
->outdent()
->write("}\n")
;
parent::compile($compiler);
$compiler
->write("if (\$namespace) {\n")
->indent()
->write("\$this->env->setNamespaceLookUpOrder(\$previous_look_up_order);\n")
->outdent()
->write("}\n")
;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_template_twig_node_includeasset extends Twig_Node
{
/** @var Twig_Environment */
protected $environment;
public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $lineno, $tag = null)
{
$this->environment = $environment;
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$config = $this->environment->get_phpbb_config();
$compiler
->write("\$asset_file = ")
->subcompile($this->getNode('expr'))
->raw(";\n")
->write("\$argument_string = \$anchor_string = '';\n")
->write("if ((\$argument_string_start = strpos(\$asset_file, '?')) !== false) {\n")
->indent()
->write("\$argument_string = substr(\$asset_file, \$argument_string_start);\n")
->write("\$asset_file = substr(\$asset_file, 0, \$argument_string_start);\n")
->write("if ((\$anchor_string_start = strpos(\$argument_string, '#')) !== false) {\n")
->indent()
->write("\$anchor_string = substr(\$argument_string, \$anchor_string_start);\n")
->write("\$argument_string = substr(\$argument_string, 0, \$anchor_string_start);\n")
->outdent()
->write("}\n")
->outdent()
->write("}\n")
->write("if (strpos(\$asset_file, '//') !== 0 && strpos(\$asset_file, 'http://') !== 0 && strpos(\$asset_file, 'https://') !== 0 && !file_exists(\$asset_file)) {\n")
->indent()
->write("\$asset_file = \$this->getEnvironment()->getLoader()->getCacheKey(\$asset_file);\n")
->write("\$argument_string .= ((\$argument_string) ? '&' : '?') . 'assets_version={$config['assets_version']}';\n")
->outdent()
->write("}\n")
->write("\$asset_file .= \$argument_string . \$anchor_string;\n")
->write("\$context['definition']->append('{$this->get_definition_name()}', '")
;
$this->append_asset($compiler);
$compiler
->raw("\n');\n")
;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_template_twig_node_includecss extends phpbb_template_twig_node_includeasset
{
public function get_definition_name()
{
return 'STYLESHEETS';
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function append_asset(Twig_Compiler $compiler)
{
$compiler
->raw("<link href=\"' . ")
->raw("\$asset_file . '\"")
->raw(' rel="stylesheet" type="text/css" media="screen, projection" />')
;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_template_twig_node_includejs extends phpbb_template_twig_node_includeasset
{
public function get_definition_name()
{
return 'SCRIPTS';
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
protected function append_asset(Twig_Compiler $compiler)
{
$config = $this->environment->get_phpbb_config();
$compiler
->raw("<script type=\"text/javascript\" src=\"' . ")
->raw("\$asset_file")
->raw(". '\"></script>\n")
;
}
}

View File

@@ -0,0 +1,91 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_includephp extends Twig_Node
{
/** @var Twig_Environment */
protected $environment;
public function __construct(Twig_Node_Expression $expr, phpbb_template_twig_environment $environment, $ignoreMissing = false, $lineno, $tag = null)
{
$this->environment = $environment;
parent::__construct(array('expr' => $expr), array('ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$config = $this->environment->get_phpbb_config();
if (!$config['tpl_allow_php'])
{
$compiler
->write("// INCLUDEPHP Disabled\n")
;
return;
}
if ($this->getAttribute('ignore_missing')) {
$compiler
->write("try {\n")
->indent()
;
}
$compiler
->write("\$location = ")
->subcompile($this->getNode('expr'))
->raw(";\n")
->write("if (phpbb_is_absolute(\$location)) {\n")
->indent()
// Absolute path specified
->write("require(\$location);\n")
->outdent()
->write("} else if (file_exists(\$this->getEnvironment()->get_phpbb_root_path() . \$location)) {\n")
->indent()
// PHP file relative to phpbb_root_path
->write("require(\$this->getEnvironment()->get_phpbb_root_path() . \$location);\n")
->outdent()
->write("} else {\n")
->indent()
// Local path (behaves like INCLUDE)
->write("require(\$this->getEnvironment()->getLoader()->getCacheKey(\$location));\n")
->outdent()
->write("}\n")
;
if ($this->getAttribute('ignore_missing')) {
$compiler
->outdent()
->write("} catch (Twig_Error_Loader \$e) {\n")
->indent()
->write("// ignore missing template\n")
->outdent()
->write("}\n\n")
;
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_node_php extends Twig_Node
{
/** @var Twig_Environment */
protected $environment;
public function __construct(Twig_Node_Text $text, phpbb_template_twig_environment $environment, $lineno, $tag = null)
{
$this->environment = $environment;
parent::__construct(array('text' => $text), array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$config = $this->environment->get_phpbb_config();
if (!$config['tpl_allow_php'])
{
$compiler
->write("// PHP Disabled\n")
;
return;
}
$compiler
->raw($this->getNode('text')->getAttribute('data'))
;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_define extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name = $this->parser->getExpressionParser()->parseExpression();
$capture = false;
if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
$stream->next();
$value = $this->parser->getExpressionParser()->parseExpression();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
} else {
$capture = true;
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$value = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
}
return new phpbb_template_twig_node_define($capture, $name, $value, $lineno, $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('ENDDEFINE');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'DEFINE';
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_event extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new phpbb_template_twig_node_event($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'EVENT';
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_include extends Twig_TokenParser_Include
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
list($variables, $only, $ignoreMissing) = $this->parseArguments();
return new phpbb_template_twig_node_include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'INCLUDE';
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
class phpbb_template_twig_tokenparser_includecss extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new phpbb_template_twig_node_includecss($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'INCLUDECSS';
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_includejs extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new phpbb_template_twig_node_includejs($expr, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'INCLUDEJS';
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group, sections (c) 2009 Fabien Potencier, Armin Ronacher
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_includephp extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$ignoreMissing = false;
if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) {
$stream->next();
$stream->expect(Twig_Token::NAME_TYPE, 'missing');
$ignoreMissing = true;
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new phpbb_template_twig_node_includephp($expr, $this->parser->getEnvironment(), $ignoreMissing, $token->getLine(), $this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'INCLUDEPHP';
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
class phpbb_template_twig_tokenparser_php extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideEnd'), true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new phpbb_template_twig_node_php($body, $this->parser->getEnvironment(), $token->getLine(), $this->getTag());
}
public function decideEnd(Twig_Token $token)
{
return $token->test('ENDPHP');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'PHP';
}
}

View File

@@ -0,0 +1,465 @@
<?php
/**
*
* @package phpBB3
* @copyright (c) 2013 phpBB Group
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
*
*/
/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
exit;
}
/**
* Twig Template class.
* @package phpBB3
*/
class phpbb_template_twig implements phpbb_template
{
/**
* Template context.
* Stores template data used during template rendering.
* @var phpbb_template_context
*/
protected $context;
/**
* Path of the cache directory for the template
*
* Cannot be changed during runtime.
*
* @var string
*/
private $cachepath = '';
/**
* phpBB root path
* @var string
*/
protected $phpbb_root_path;
/**
* adm relative path
* @var string
*/
protected $adm_relative_path;
/**
* PHP file extension
* @var string
*/
protected $php_ext;
/**
* phpBB config instance
* @var phpbb_config
*/
protected $config;
/**
* Current user
* @var phpbb_user
*/
protected $user;
/**
* Extension manager.
*
* @var phpbb_extension_manager
*/
protected $extension_manager;
/**
* Name of the style that the template being compiled and/or rendered
* belongs to, and its parents, in inheritance tree order.
*
* Used to invoke style-specific template events.
*
* @var array
*/
protected $style_names;
/**
* Twig Environment
*
* @var Twig_Environment
*/
protected $twig;
/**
* Array of filenames assigned to set_filenames
*
* @var array
*/
protected $filenames = array();
/**
* Constructor.
*
* @param string $phpbb_root_path phpBB root path
* @param string $php_ext php extension (typically 'php')
* @param phpbb_config $config
* @param phpbb_user $user
* @param phpbb_template_context $context template context
* @param phpbb_extension_manager $extension_manager extension manager, if null then template events will not be invoked
* @param string $adm_relative_path relative path to adm directory
*/
public function __construct($phpbb_root_path, $php_ext, $config, $user, phpbb_template_context $context, phpbb_extension_manager $extension_manager = null, $adm_relative_path = null)
{
$this->phpbb_root_path = $phpbb_root_path;
$this->adm_relative_path = $adm_relative_path;
$this->php_ext = $php_ext;
$this->config = $config;
$this->user = $user;
$this->context = $context;
$this->extension_manager = $extension_manager;
$this->cachepath = $phpbb_root_path . 'cache/twig/';
// Initiate the loader, __main__ namespace paths will be setup later in set_style_names()
$loader = new Twig_Loader_Filesystem('');
$this->twig = new phpbb_template_twig_environment(
$this->config,
($this->extension_manager) ? $this->extension_manager->all_enabled() : array(),
$this->phpbb_root_path,
$loader,
array(
'cache' => (defined('IN_INSTALL')) ? false : $this->cachepath,
'debug' => defined('DEBUG'),
'auto_reload' => (bool) $this->config['load_tplcompile'],
'autoescape' => false,
)
);
$this->twig->addExtension(
new phpbb_template_twig_extension(
$this->context,
$this->user
)
);
$lexer = new phpbb_template_twig_lexer($this->twig);
$this->twig->setLexer($lexer);
}
/**
* Clear the cache
*
* @return phpbb_template
*/
public function clear_cache()
{
if (is_dir($this->cachepath))
{
$this->twig->clearCacheFiles();
}
return $this;
}
/**
* Sets the template filenames for handles.
*
* @param array $filename_array Should be a hash of handle => filename pairs.
* @return phpbb_template $this
*/
public function set_filenames(array $filename_array)
{
$this->filenames = array_merge($filename_array, $this->filenames);
return $this;
}
/**
* Sets the style names/paths corresponding to style hierarchy being compiled
* and/or rendered.
*
* @param array $style_names List of style names in inheritance tree order
* @param array $style_paths List of style paths in inheritance tree order
* @param bool $is_core True if the style names are the "core" styles for this page load
* Core means the main phpBB template files
* @return phpbb_template $this
*/
public function set_style_names(array $style_names, array $style_paths, $is_core = false)
{
$this->style_names = $style_names;
// Set as __main__ namespace
$this->twig->getLoader()->setPaths($style_paths);
// Core style namespace from phpbb_style::set_style()
if ($is_core)
{
$this->twig->getLoader()->setPaths($style_paths, 'core');
}
// Add admin namespace
if (is_dir($this->phpbb_root_path . $this->adm_relative_path . 'style/'))
{
$this->twig->getLoader()->setPaths($this->phpbb_root_path . $this->adm_relative_path . 'style/', 'admin');
}
// Add all namespaces for all extensions
if ($this->extension_manager instanceof phpbb_extension_manager)
{
$style_names[] = 'all';
foreach ($this->extension_manager->all_enabled() as $ext_namespace => $ext_path)
{
// namespaces cannot contain /
$namespace = str_replace('/', '_', $ext_namespace);
$paths = array();
foreach ($style_names as $style_name)
{
$ext_style_path = $ext_path . 'styles/' . $style_name . '/template';
if (is_dir($ext_style_path))
{
$paths[] = $ext_style_path;
}
}
$this->twig->getLoader()->setPaths($paths, $namespace);
}
}
return $this;
}
/**
* Clears all variables and blocks assigned to this template.
*
* @return phpbb_template $this
*/
public function destroy()
{
$this->context = array();
return $this;
}
/**
* Reset/empty complete block
*
* @param string $blockname Name of block to destroy
* @return phpbb_template $this
*/
public function destroy_block_vars($blockname)
{
$this->context->destroy_block_vars($blockname);
return $this;
}
/**
* Display a template for provided handle.
*
* The template will be loaded and compiled, if necessary, first.
*
* This function calls hooks.
*
* @param string $handle Handle to display
* @return phpbb_template $this
*/
public function display($handle)
{
$result = $this->call_hook($handle, __FUNCTION__);
if ($result !== false)
{
return $result[0];
}
$this->twig->display($this->get_filename_from_handle($handle), $this->get_template_vars());
return $this;
}
/**
* Calls hook if any is defined.
*
* @param string $handle Template handle being displayed.
* @param string $method Method name of the caller.
*/
protected function call_hook($handle, $method)
{
global $phpbb_hook;
if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, $method), $handle, $this))
{
if ($phpbb_hook->hook_return(array(__CLASS__, $method)))
{
$result = $phpbb_hook->hook_return_result(array(__CLASS__, $method));
return array($result);
}
}
return false;
}
/**
* Display the handle and assign the output to a template variable
* or return the compiled result.
*
* @param string $handle Handle to operate on
* @param string $template_var Template variable to assign compiled handle to
* @param bool $return_content If true return compiled handle, otherwise assign to $template_var
* @return phpbb_template|string if $return_content is true return string of the compiled handle, otherwise return $this
*/
public function assign_display($handle, $template_var = '', $return_content = true)
{
if ($return_content)
{
return $this->twig->render($this->get_filename_from_handle($handle), $this->get_template_vars());
}
$this->assign_var($template_var, $this->twig->render($this->get_filename_from_handle($handle, $this->get_template_vars())));
return $this;
}
/**
* Assign key variable pairs from an array
*
* @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/
public function assign_vars(array $vararray)
{
foreach ($vararray as $key => $val)
{
$this->assign_var($key, $val);
}
return $this;
}
/**
* Assign a single scalar value to a single key.
*
* Value can be a string, an integer or a boolean.
*
* @param string $varname Variable name
* @param string $varval Value to assign to variable
* @return phpbb_template $this
*/
public function assign_var($varname, $varval)
{
$this->context->assign_var($varname, $varval);
return $this;
}
/**
* Append text to the string value stored in a key.
*
* Text is appended using the string concatenation operator (.).
*
* @param string $varname Variable name
* @param string $varval Value to append to variable
* @return phpbb_template $this
*/
public function append_var($varname, $varval)
{
$this->context->append_var($varname, $varval);
return $this;
}
/**
* Assign key variable pairs from an array to a specified block
* @param string $blockname Name of block to assign $vararray to
* @param array $vararray A hash of variable name => value pairs
* @return phpbb_template $this
*/
public function assign_block_vars($blockname, array $vararray)
{
$this->context->assign_block_vars($blockname, $vararray);
return $this;
}
/**
* Change already assigned key variable pair (one-dimensional - single loop entry)
*
* An example of how to use this function:
* {@example alter_block_array.php}
*
* @param string $blockname the blockname, for example 'loop'
* @param array $vararray the var array to insert/add or merge
* @param mixed $key Key to search for
*
* array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position]
*
* int: Position [the position to change or insert at directly given]
*
* If key is false the position is set to 0
* If key is true the position is set to the last entry
*
* @param string $mode Mode to execute (valid modes are 'insert' and 'change')
*
* If insert, the vararray is inserted at the given position (position counting from zero).
* If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value).
*
* Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array)
* and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars)
*
* @return bool false on error, true on success
*/
public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert')
{
return $this->context->alter_block_array($blockname, $vararray, $key, $mode);
}
/**
* Get template vars in a format Twig will use (from the context)
*
* @return array
*/
public function get_template_vars()
{
$context_vars = $this->context->get_data_ref();
$vars = array_merge(
$context_vars['.'][0], // To get normal vars
$context_vars, // To get loops
array(
'definition' => new phpbb_template_twig_definition(),
'user' => $this->user,
)
);
// cleanup
unset($vars['.']);
return $vars;
}
/**
* Get a filename from the handle
*
* @param string $handle
* @return string
*/
protected function get_filename_from_handle($handle)
{
return (isset($this->filenames[$handle])) ? $this->filenames[$handle] : $handle;
}
/**
* Get path to template for handle (required for BBCode parser)
*
* @return string
*/
public function get_source_file_for_handle($handle)
{
return $this->twig->getLoader()->getCacheKey($this->get_filename_from_handle($handle));
}
}

View File

@@ -565,7 +565,7 @@ class ucp_groups
if ($colour_error = validate_data($submit_ary, array('colour' => array('hex_colour', true))))
{
// Replace "error" string with its real, localised form
$error = array_merge($error, array_map(array(&$user, 'lang'), $colour_error));
$error = array_merge($error, $colour_error);
}
if (!sizeof($error))
@@ -613,6 +613,7 @@ class ucp_groups
if (sizeof($error))
{
$error = array_map(array(&$user, 'lang'), $error);
$group_rank = $submit_ary['rank'];
$group_desc_data = array(