diff options
Diffstat (limited to 'phpBB/phpbb/template')
-rw-r--r-- | phpBB/phpbb/template/asset.php | 13 | ||||
-rw-r--r-- | phpBB/phpbb/template/base.php | 22 | ||||
-rw-r--r-- | phpBB/phpbb/template/twig/extension/icon.php | 321 | ||||
-rw-r--r-- | phpBB/phpbb/template/twig/loader.php | 18 | ||||
-rw-r--r-- | phpBB/phpbb/template/twig/node/includeasset.php | 2 | ||||
-rw-r--r-- | phpBB/phpbb/template/twig/twig.php | 6 |
6 files changed, 333 insertions, 49 deletions
diff --git a/phpBB/phpbb/template/asset.php b/phpBB/phpbb/template/asset.php index d6b46234f0..5fc3e4acaf 100644 --- a/phpBB/phpbb/template/asset.php +++ b/phpBB/phpbb/template/asset.php @@ -13,6 +13,8 @@ namespace phpbb\template; +use phpbb\filesystem\helper as filesystem_helper; + class asset { protected $components = array(); @@ -20,20 +22,15 @@ class asset /** @var \phpbb\path_helper **/ protected $path_helper; - /** @var \phpbb\filesystem\filesystem */ - protected $filesystem; - /** * Constructor * * @param string $url URL * @param \phpbb\path_helper $path_helper Path helper object - * @param \phpbb\filesystem\filesystem $filesystem */ - public function __construct($url, \phpbb\path_helper $path_helper, \phpbb\filesystem\filesystem $filesystem) + public function __construct($url, \phpbb\path_helper $path_helper) { $this->path_helper = $path_helper; - $this->filesystem = $filesystem; $this->set_url($url); } @@ -151,7 +148,7 @@ class asset public function set_path($path, $urlencode = false) { // Since 1.7.0 Twig returns the real path of the file. We need it to be relative. - $real_root_path = $this->filesystem->realpath($this->path_helper->get_phpbb_root_path()) . DIRECTORY_SEPARATOR; + $real_root_path = filesystem_helper::realpath($this->path_helper->get_phpbb_root_path()) . DIRECTORY_SEPARATOR; // If the asset is under the phpBB root path we need to remove its path and then prepend $phpbb_root_path if ($real_root_path && substr($path . DIRECTORY_SEPARATOR, 0, strlen($real_root_path)) === $real_root_path) @@ -161,7 +158,7 @@ class asset else { // Else we make the path relative to the current working directory - $real_root_path = $this->filesystem->realpath('.') . DIRECTORY_SEPARATOR; + $real_root_path = filesystem_helper::realpath('.') . DIRECTORY_SEPARATOR; if ($real_root_path && substr($path . DIRECTORY_SEPARATOR, 0, strlen($real_root_path)) === $real_root_path) { $path = str_replace('\\', '/', substr($path, strlen($real_root_path))); diff --git a/phpBB/phpbb/template/base.php b/phpBB/phpbb/template/base.php index d502aceab8..314cdc4796 100644 --- a/phpBB/phpbb/template/base.php +++ b/phpBB/phpbb/template/base.php @@ -168,26 +168,4 @@ abstract class base implements template { return $this->context->find_key_index($blockname, $key); } - - /** - * 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('template', $method), $handle, $this)) - { - if ($phpbb_hook->hook_return(array('template', $method))) - { - $result = $phpbb_hook->hook_return_result(array('template', $method)); - return array($result); - } - } - - return false; - } } diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php new file mode 100644 index 0000000000..f96ed94821 --- /dev/null +++ b/phpBB/phpbb/template/twig/extension/icon.php @@ -0,0 +1,321 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\template\twig\extension; + +use phpbb\template\twig\environment; + +class icon extends \Twig\Extension\AbstractExtension +{ + /** @var \phpbb\user */ + protected $user; + + /** + * Constructor. + * + * @param \phpbb\user $user User object + */ + public function __construct(\phpbb\user $user) + { + $this->user = $user; + } + + /** + * Returns the name of this extension. + * + * @return string The extension name + */ + public function getName() + { + return 'icon'; + } + + /** + * Returns a list of functions to add to the existing list. + * + * @return \Twig\TwigFunction[] Array of twig functions + */ + public function getFunctions() + { + return [ + new \Twig\TwigFunction('Icon', [$this, 'icon'], ['needs_environment' => true]), + ]; + } + + /** + * Generate icon HTML for use in the template, depending on the mode. + * + * @param environment $environment Twig environment object + * @param string $type Icon type (font|iconify|png|svg) + * @param string $icon Icon name (eg. "bold") + * @param string $title Icon title + * @param bool $hidden Hide the icon title from view + * @param string $classes Additional classes (eg. "fa-fw") + * @param array $attributes Additional attributes for the icon, where the key is the attribute. + * {'data-ajax': 'mark_forums'} results in ' data-ajax="mark_forums"' + * @return string + */ + public function icon(environment $environment, $type, $icon, $title = '', $hidden = false, $classes = '', array $attributes = []) + { + $type = strtolower($type); + $icon = is_array($icon) ? $this->get_first_icon($icon) : $icon; + + if (empty($icon)) + { + return ''; + } + + $not_found = false; + $source = ''; + $view_box = ''; + + switch ($type) + { + case 'font': + // Nothing to do here.. + break; + + case 'iconify': + $source = explode(':', $icon); + $source = $source[0]; + break; + + case 'png': + $filesystem = $environment->get_filesystem(); + $root_path = $environment->get_web_root_path(); + + $board_url = defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH; + $base_path = $board_url ? generate_board_url() . '/' : $root_path; + + // Iterate over the user's styles and check for icon existance + foreach ($this->get_style_list() as $style_path) + { + if ($filesystem->exists("{$root_path}styles/{$style_path}/theme/png/{$icon}.png")) + { + $source = "{$base_path}styles/{$style_path}/theme/png/{$icon}.png"; + + break; + } + } + + // Check if the icon was found or not + $not_found = empty($source); + break; + + case 'svg': + try + { + // Try to load and prepare the SVG icon + $file = $environment->load('svg/' . $icon . '.svg'); + $source = $this->prepare_svg($file, $view_box); + + if (empty($view_box)) + { + return ''; + } + } + catch (\Twig\Error\LoaderError $e) + { + // Icon was not found + $not_found = true; + } + catch (\Twig\Error\Error $e) + { + return ''; + } + break; + + default: + return ''; + break; + } + + // If no PNG or SVG icon was found, display a default 404 SVG icon. + if ($not_found) + { + try + { + $file = $environment->load('svg/404.svg'); + $source = $this->prepare_svg($file, $view_box); + } + catch (\Twig\Error\Error $e) + { + return ''; + } + + $type = 'svg'; + $icon = '404'; + } + + try + { + return $environment->render("macros/icons/{$type}.twig", [ + 'ATTRIBUTES' => (string) $this->implode_attributes($attributes), + 'CLASSES' => (string) $classes, + 'ICON' => (string) $icon, + 'SOURCE' => (string) $source, + 'TITLE' => (string) $title, + 'VIEW_BOX' => (string) $view_box, + 'S_HIDDEN' => (bool) $hidden, + ]); + } + catch (\Twig\Error\Error $e) + { + return ''; + } + } + + /** + * Prepare an SVG for usage in the template icon. + * + * This removes any <?xml ?> and <!DOCTYPE> elements, + * aswell as the root <svg> and any <title> elements. + * + * @param \Twig\TemplateWrapper $file The SVG file loaded from the environment + * @param string $view_box The viewBox attribute value + * @return string The cleaned SVG + */ + protected function prepare_svg(\Twig\TemplateWrapper $file, &$view_box = '') + { + $code = $file->render(); + $code = preg_replace( "/<\?xml.+?\?>/", '', $code); + + $doc = new \DOMDocument(); + $doc->preserveWhiteSpace = false; + + /** + * Suppression is needed as DOMDocument does not like HTML5 and SVGs. + * Options parameter prevents $dom->saveHTML() from adding an <html> element. + */ + @$doc->loadHTML($code, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); + + // Remove any DOCTYPE + foreach ($doc->childNodes as $child) + { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) + { + $child->parentNode->removeChild($child); + } + } + + $xpath = new \DOMXPath($doc); + + /** + * Remove the root <svg> element + * and all <title> elements. + * + * @var \DOMElement $element + */ + foreach ($xpath->query('/svg | //title') as $element) + { + if ($element->nodeName === 'svg') + { + // Return the viewBox attribute value of the root SVG element by reference + $view_box = $element->getAttribute('viewbox'); + + $width = $element->getAttribute('width'); + $height = $element->getAttribute('height'); + + if (empty($view_box) && $width && $height) + { + $view_box = "0 0 {$width} {$height}"; + } + + while (isset($element->firstChild)) + { + $element->parentNode->insertBefore($element->firstChild, $element); + } + } + + $element->parentNode->removeChild($element); + } + + $string = $doc->saveHTML(); + $string = preg_replace('/\s+/', ' ', $string); + + return $string; + } + + /** + * Finds the first icon that has a "true" value and returns it. + * + * This allows sending an array to the Icon() function, + * where the keys are the icon names and the values are their checks. + * + * {{ Icon('font', { + * 'bullhorn': topicrow.S_POST_GLOBAL or topicrow.S_POST_ANNOUNCE, + * 'star': topicrow.S_POST_STICKY, + * 'lock': topicrow.S_TOPIC_LOCKED, + * 'fire': topicrow.S_TOPIC_HOT, + * 'file': true, + * }, 'MY_TITLE', true) }} + * + * @param array $icons Array of icons and their booleans + * @return string The first 'true' icon + */ + protected function get_first_icon(array $icons) + { + foreach ($icons as $icon => $boolean) + { + // In case the key is not a string, + // this icon does not have a check + // so instantly return it + if (!is_string($icon)) + { + return $boolean; + } + + if ($boolean) + { + return $icon; + } + } + + return ''; + } + + /** + * Implode an associated array of attributes to a string for usage in a template. + * + * @param array $attributes Associated array of attributes + * @return string + */ + protected function implode_attributes(array $attributes) + { + $string = ''; + + foreach ($attributes as $key => $value) + { + $string .= ' ' . $key . '="' . $value . '"'; + } + + return $string; + } + + /** + * Get the style tree of the style preferred by the current user. + * + * @return array Style tree, most specific first + */ + protected function get_style_list() + { + $style_list = [$this->user->style['style_path']]; + + if ($this->user->style['style_parent_id']) + { + $style_list = array_merge($style_list, array_reverse(explode('/', $this->user->style['style_parent_tree']))); + } + + return $style_list; + } +} diff --git a/phpBB/phpbb/template/twig/loader.php b/phpBB/phpbb/template/twig/loader.php index 0f193dbe59..cddcf33146 100644 --- a/phpBB/phpbb/template/twig/loader.php +++ b/phpBB/phpbb/template/twig/loader.php @@ -13,6 +13,8 @@ namespace phpbb\template\twig; +use phpbb\filesystem\helper as filesystem_helper; + /** * Twig Template loader */ @@ -21,20 +23,12 @@ class loader extends \Twig_Loader_Filesystem protected $safe_directories = array(); /** - * @var \phpbb\filesystem\filesystem_interface - */ - protected $filesystem; - - /** * Constructor * - * @param \phpbb\filesystem\filesystem_interface $filesystem * @param string|array $paths */ - public function __construct(\phpbb\filesystem\filesystem_interface $filesystem, $paths = array()) + public function __construct($paths = array()) { - $this->filesystem = $filesystem; - parent::__construct($paths, __DIR__); } @@ -67,7 +61,7 @@ class loader extends \Twig_Loader_Filesystem */ public function addSafeDirectory($directory) { - $directory = $this->filesystem->realpath($directory); + $directory = filesystem_helper::realpath($directory); if ($directory !== false) { @@ -107,7 +101,7 @@ class loader extends \Twig_Loader_Filesystem */ public function addPath($path, $namespace = self::MAIN_NAMESPACE) { - return parent::addPath($this->filesystem->realpath($path), $namespace); + return parent::addPath(filesystem_helper::realpath($path), $namespace); } /** @@ -147,7 +141,7 @@ class loader extends \Twig_Loader_Filesystem // can now check if we're within a "safe" directory // Find the real path of the directory the file is in - $directory = $this->filesystem->realpath(dirname($file)); + $directory = filesystem_helper::realpath(dirname($file)); if ($directory === false) { diff --git a/phpBB/phpbb/template/twig/node/includeasset.php b/phpBB/phpbb/template/twig/node/includeasset.php index 69bfd58803..bf29fa7277 100644 --- a/phpBB/phpbb/template/twig/node/includeasset.php +++ b/phpBB/phpbb/template/twig/node/includeasset.php @@ -49,7 +49,7 @@ abstract class includeasset extends \Twig_Node ->write("\n") ->write("if (\$asset->is_relative()) {\n") ->indent() - ->write("\$asset->add_assets_version(\$this->env->get_phpbb_config()['assets_version']);\n") + ->write("\$asset->add_assets_version(\$this->env->get_phpbb_config()['assets_version']);\n") ->outdent() ->write("}\n") ->write("\$this->env->get_assets_bag()->add_{$this->get_setters_name()}(\$asset);") diff --git a/phpBB/phpbb/template/twig/twig.php b/phpBB/phpbb/template/twig/twig.php index f322778eda..f0736045de 100644 --- a/phpBB/phpbb/template/twig/twig.php +++ b/phpBB/phpbb/template/twig/twig.php @@ -308,12 +308,6 @@ class twig extends \phpbb\template\base */ 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; |