diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml index 3e3d1a4674..8c75d792b1 100644 --- a/phpBB/config/default/container/services_twig.yml +++ b/phpBB/config/default/container/services_twig.yml @@ -57,6 +57,7 @@ services: template.twig.extensions.icon: class: phpbb\template\twig\extension\icon arguments: + - '@template.twig.environment' - '@user' tags: - { name: twig.extension } diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php index 3611f19042..dee48908ee 100644 --- a/phpBB/phpbb/template/twig/extension/icon.php +++ b/phpBB/phpbb/template/twig/extension/icon.php @@ -1,165 +1,120 @@ -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited + * @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 +abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface { - /** @var \phpbb\user */ + protected $twig; protected $user; - /** - * Constructor. - * - * @param \phpbb\user $user User object - */ - public function __construct(\phpbb\user $user) + public function __construct(environment $twig, \phpbb\user $user) { + $this->twig = $twig; $this->user = $user; } /** - * Get the name of this extension. - * - * @return string - */ + * Get the name of this extension. + * + * @return string + */ public function getName() { return 'icon'; } /** - * Returns a list of global functions to add to the existing list. - * - * @return array An array of global functions - */ - public function getFunctions() - { - return [ - new \Twig_SimpleFunction('Icon', [$this, 'icon'], ['needs_environment' => true]), - ]; - } - - /** - * Generate icon HTML for use in the template, depending on the mode. + * Returns a list of global variables to add to the existing list. * - * @param environment $environment Twig environment object - * @param string $type Icon type (font|png|svg) - * @param string $icon Icon name (eg. "bold") - * @param string $classes Additional classes (eg. "fa-fw") - * @param string $title Icon title - * @param bool $hidden Hide the icon title from view - * @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 + * @return array An array of global variables */ - public function icon(environment $environment, $type = '', $icon = '', $classes = '', $title = '', $hidden = false, array $attributes = []) + public function getGlobals() { - switch ($type) - { - case 'font': - $src = ''; - break; - - case 'png': - $board_url = defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH; - $web_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path(); - - $src = "{$web_path}styles/" . $this->user->style['style_path'] . "/template/icons/png/{$icon}.png"; - break; - - case 'svg': - try - { - $file = $environment->load('icons/svg/' . $icon . '.svg'); - } - catch (\Twig_Error $e) - { - return ''; - } - - $src = $this->filter_svg($file); - break; - - default: - // Not a supported icon type (font|png|svg), return an empty string - return ''; - break; - } + $macros = null; try { - return $environment->render("icons/{$type}.html", [ - 'ATTRIBUTES' => (string) $this->implode_attributes($attributes), - 'CLASSES' => (string) $classes, - 'ICON' => (string) $icon, - 'SOURCE' => (string) $src, - 'TITLE' => (string) $title, - 'S_HIDDEN' => (bool) $hidden, - ]); + $macros = $this->twig->loadTemplate('macros.html'); } - catch (\Twig_Error $e) + catch (\Twig\Error\Error $e) + { + } + + return [ + 'macro' => $macros, + ]; + } + + public function getFilters() + { + return [ + new \Twig\TwigFilter('Png_path', [$this, 'png_path'], ['needs_environment' => true]), + ]; + } + + public function getFunctions() + { + return [ + new \Twig\TwigFunction('Svg_clean', [$this, 'svg_clean'], ['needs_environment' => true]), + new \Twig\TwigFunction('Implode_attributes', [$this, 'implode_attributes']), + new \Twig\TwigFunction('Implode_classes', [$this, 'implode_classes']), + ]; + } + + protected function png_path(environment $environment, $icon) + { + $board_url = defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH; + $web_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path(); + $style_path = $this->user->style['style_path']; + + return "{$web_path}styles/{$style_path}/template/icons/png/{$icon}.png"; + } + + protected function svg_clean(environment $environment, $icon) + { + try + { + $file = $environment->load('icons/svg/' . $icon . '.svg'); + } + catch (\Twig\Error\Error $e) { 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) - { - $attr_str = ''; - - foreach ($attributes as $attribute => $value) - { - $attr_str .= ' ' . $attribute . '="' . $value . '"'; - } - - return $attr_str; - } - - /** - * Filter a SVG for usage in the template. - * - * @param \Twig_TemplateWrapper $file The svg file loaded from the environment - * @return string - */ - protected function filter_svg(\Twig_TemplateWrapper $file) - { - /** @var \Twig_Source $src */ $src = $file->getSourceContext(); $svg = $src->getCode(); - /** @var \DOMDocument $dom */ - $dom = new \DOMDocument(); - $dom->preserveWhiteSpace = false; + $doc = new \DOMDocument(); + $doc->formatOutput = false; + $doc->preserveWhiteSpace = false; + $doc->strictErrorChecking = false; - /** - * Suppression is needed as DOMDocument does not like HTML5 and SVGs. - * Options parameter prevents $dom->saveHTML() from adding an element. - */ - @$dom->loadHTML($svg, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); + if (!$doc->loadXML($svg)) + { + return ''; + } - /** @var \DOMXPath $xpath */ - $xpath = new \DOMXPath($dom); + foreach ($doc->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + $child->parentNode->removeChild($child); + } + } + + $xpath = new \DOMXPath($doc); - /** @var \DOMNode $element */ foreach ($xpath->query('//svg | //title') as $element) { if ($element->nodeName === 'svg') @@ -182,17 +137,95 @@ class icon extends \Twig_Extension $element->parentNode->removeChild($element); } - /** @var \DOMElement $attribute */ - foreach ($xpath->query('//@fill') as $attribute) - { - if ($attribute->nodeName === 'fill' && $attribute->nodeValue === 'none') - { - continue; - } + $string = $doc->saveXML($doc->documentElement, LIBXML_NOEMPTYTAG); + $string = preg_replace('/\s+/', ' ', $string); - $attribute->parentNode->removeAttribute($attribute->nodeName); + return $string; + } + + protected function implode_attributes(...$arguments) + { + $string = ''; + $attributes = []; + + foreach ($arguments as $argument) + { + if (is_string($argument)) + { + $attributes[] = $argument; + } + else if (is_array($argument)) + { + foreach ($argument as $key => $value) + { + if (is_integer($key) && is_string($value)) + { + $attributes[] = $value; + } + else + { + $attributes[$key] = $value; + } + } + } } - return $dom->saveHTML(); + foreach ($attributes as $attribute => $value) + { + if (is_string($attribute)) + { + $string .= ' ' . $attribute . '="' . $value . '"'; + } + else + { + $string .= ' ' . $attribute; + } + } + + return $string; + } + + protected function implode_classes(...$arguments) + { + $classes = []; + + foreach ($arguments as $argument) + { + if (is_string($argument)) + { + $classes[] = $argument; + } + else if (is_array($argument)) + { + foreach ($argument as $key => $value) + { + if (is_integer($key) && is_string($value)) + { + $classes[] = $value; + } + else if (is_string($key)) + { + if ($value) + { + $classes[] = $key; + } + } + else if (is_array($value)) + { + foreach ($value as $class => $condition) + { + if ($condition) + { + $classes[] = $class; + } + } + } + } + } + } + + $string = implode(' ', array_unique($classes)); + + return $string ? ' ' . $string : $string; } } diff --git a/phpBB/styles/all/template/macros.html b/phpBB/styles/all/template/macros.html new file mode 100644 index 0000000000..aaa9990763 --- /dev/null +++ b/phpBB/styles/all/template/macros.html @@ -0,0 +1,33 @@ +{# Wrapper function #} +{% macro Icon(type, icon, classes = '', title = '', hidden = false, attributes = {}) %} + {% set type = type|capitalize %} + + {% if type in ['Fa', 'Png', 'Svg'] %} + {{ attribute(_self, type, [icon, classes, title, hidden, attributes]) }} + {% endif %} + +{% endmacro Icon %} + +{# FA icons #} +{% macro Fa(icon, classes = '', title = '', hidden = false, attributes = {}) %} + + {% if title %}