From 9122d1afd3628f8d68c304210e1fe7c4cdff9d55 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Fri, 8 Mar 2019 22:25:45 +0100
Subject: [PATCH 01/25] [ticket/15538] Twig icon function

PHPBB3-15538
---
 .../default/container/services_twig.yml       |   7 +
 phpBB/phpbb/template/twig/extension/icon.php  | 198 ++++++++++++++++++
 .../styles/prosilver/template/icons/font.html |  11 +
 .../styles/prosilver/template/icons/png.html  |   8 +
 .../prosilver/template/icons/png/bars.png     | Bin 0 -> 85 bytes
 .../prosilver/template/icons/png/envelope.png | Bin 0 -> 189 bytes
 .../prosilver/template/icons/png/pencil.png   | Bin 0 -> 165 bytes
 .../prosilver/template/icons/png/phone.png    | Bin 0 -> 195 bytes
 .../styles/prosilver/template/icons/svg.html  |  21 ++
 .../prosilver/template/icons/svg/bars.svg     |   1 +
 .../prosilver/template/icons/svg/envelope.svg |   1 +
 .../prosilver/template/icons/svg/pencil.svg   |   1 +
 .../prosilver/template/icons/svg/phone.svg    |   1 +
 .../styles/prosilver/template/index_body.html |  32 ++-
 phpBB/styles/prosilver/theme/icons.css        |  21 ++
 15 files changed, 301 insertions(+), 1 deletion(-)
 create mode 100644 phpBB/phpbb/template/twig/extension/icon.php
 create mode 100644 phpBB/styles/prosilver/template/icons/font.html
 create mode 100644 phpBB/styles/prosilver/template/icons/png.html
 create mode 100644 phpBB/styles/prosilver/template/icons/png/bars.png
 create mode 100644 phpBB/styles/prosilver/template/icons/png/envelope.png
 create mode 100644 phpBB/styles/prosilver/template/icons/png/pencil.png
 create mode 100644 phpBB/styles/prosilver/template/icons/png/phone.png
 create mode 100644 phpBB/styles/prosilver/template/icons/svg.html
 create mode 100644 phpBB/styles/prosilver/template/icons/svg/bars.svg
 create mode 100644 phpBB/styles/prosilver/template/icons/svg/envelope.svg
 create mode 100644 phpBB/styles/prosilver/template/icons/svg/pencil.svg
 create mode 100644 phpBB/styles/prosilver/template/icons/svg/phone.svg

diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml
index 24ee24484b..3e3d1a4674 100644
--- a/phpBB/config/default/container/services_twig.yml
+++ b/phpBB/config/default/container/services_twig.yml
@@ -54,6 +54,13 @@ services:
         tags:
             - { name: twig.extension }
 
+    template.twig.extensions.icon:
+        class: phpbb\template\twig\extension\icon
+        arguments:
+            - '@user'
+        tags:
+            - { name: twig.extension }
+
     template.twig.extensions.routing:
         class: phpbb\template\twig\extension\routing
         arguments:
diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
new file mode 100644
index 0000000000..be83222a62
--- /dev/null
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -0,0 +1,198 @@
+<?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
+{
+	/** @var \phpbb\user */
+	protected $user;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param \phpbb\user	$user		User object
+	 */
+	public function __construct(\phpbb\user $user)
+	{
+		$this->user = $user;
+	}
+
+	/**
+	* 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.
+	 *
+	 * @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
+	 */
+	public function icon(environment $environment, $type = '', $icon = '', $classes = '', $title = '', $hidden = false, array $attributes = [])
+	{
+		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;
+		}
+
+		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,
+			]);
+		}
+		catch (\Twig_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;
+
+		/**
+		 * Suppression is needed as DOMDocument does not like HTML5 and SVGs.
+		 * Options parameter prevents $dom->saveHTML() from adding an <html> element.
+		 */
+		@$dom->loadHTML($svg, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+
+		/** @var \DOMXPath $xpath */
+		$xpath = new \DOMXPath($dom);
+
+		/** @var \DOMNode $element */
+		foreach ($xpath->query('//svg | //title') as $element)
+		{
+			if ($element->nodeName === 'svg')
+			{
+				$children = [];
+
+				/** @var \DOMNode $node */
+				foreach ($element->childNodes as $node)
+				{
+					$children[] = $node;
+				}
+
+				/** @var \DOMNode $child */
+				foreach ($children as $child)
+				{
+					$element->parentNode->insertBefore($child, $element);
+				}
+			}
+
+			$element->parentNode->removeChild($element);
+		}
+
+		/** @var \DOMElement $attribute */
+		foreach ($xpath->query('//@fill') as $attribute)
+		{
+			if ($attribute->nodeName === 'fill' && $attribute->nodeValue === 'none')
+			{
+				continue;
+			}
+
+			$attribute->parentNode->removeAttribute($attribute->nodeName);
+		}
+
+		return $dom->saveHTML();
+	}
+}
diff --git a/phpBB/styles/prosilver/template/icons/font.html b/phpBB/styles/prosilver/template/icons/font.html
new file mode 100644
index 0000000000..488d434fa9
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/font.html
@@ -0,0 +1,11 @@
+{% spaceless %}
+<i
+	class="icon fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	{% if S_HIDDEN %}
+		{% if TITLE %}title="{{ lang(TITLE) }}"{% endif %}
+		aria-hidden="true"
+	{% endif %}
+	{{ ATTRIBUTES }}
+></i>
+{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/png.html b/phpBB/styles/prosilver/template/icons/png.html
new file mode 100644
index 0000000000..dafdece71e
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/png.html
@@ -0,0 +1,8 @@
+{% spaceless %}
+<img
+	class="icon png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	src="{{ SOURCE }}"
+	alt="{{ lang(TITLE) }}"
+	{{ ATTRIBUTES }}
+/>
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/png/bars.png b/phpBB/styles/prosilver/template/icons/png/bars.png
new file mode 100644
index 0000000000000000000000000000000000000000..e8dcba56ec85c8e45837462b1a4702601384ca8d
GIT binary patch
literal 85
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|8BZ6-kP60R1<szP|4$NqE;xuS
i)0W^}e9=IPmEn!L-SrhZ1qXrZ7(8A5T-G@yGywnwjurR-

literal 0
HcmV?d00001

diff --git a/phpBB/styles/prosilver/template/icons/png/envelope.png b/phpBB/styles/prosilver/template/icons/png/envelope.png
new file mode 100644
index 0000000000000000000000000000000000000000..d435a9b648c8da1c64eff9788c39a63b507e361c
GIT binary patch
literal 189
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mT0C7GLn;{eUf9Un6d=&>P=DGn
zPM$Rfq)d2P%ocIKP@84I=^WtTz1nhj_=@$$ckb7fAC0W{+I+%9yD{uVsr{=~<^rL4
zdD|Egp1)Ep5z6XuGi|t)am8bWaOUU4^#%UZ?FB;Ex{oekQYjH*FpFBt`$X`8bnL&j
nKBwuLwwnz)eI{SJ^_lI0RQUeoy5iA5_b_<6`njxgN@xNAZSX~h

literal 0
HcmV?d00001

diff --git a/phpBB/styles/prosilver/template/icons/png/pencil.png b/phpBB/styles/prosilver/template/icons/png/pencil.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ec4db8f9dbc646713f4f18a379b0cbcccb0c0c8
GIT binary patch
literal 165
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|may(reLn;{eo>CNYaFA%YxPIlN
zWmYXqG`l1n3)OpX7YLnY6j9c(->iHqHu=M+$Lx~4^Y)6Y;h4MjCkqdUQ?{0&u%)7M
z*Oibo7FSP~(paHbm+coqrftrdqAyaq{7FCemv==f<&D2GY+2{uW{i-xJ{9}T-V<mq
NgQu&X%Q~loCICznI8p!r

literal 0
HcmV?d00001

diff --git a/phpBB/styles/prosilver/template/icons/png/phone.png b/phpBB/styles/prosilver/template/icons/png/phone.png
new file mode 100644
index 0000000000000000000000000000000000000000..7a7e756311d53eb3be3eb55b2ec236b7a991379f
GIT binary patch
literal 195
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mx;$MRLn;{8o@!)u3S@A&IN8Kc
z=^~4+AR`N>qv8~)g$qO+xJ4FxiC!eY!N|0rL;PpcC%Y2817Fj9JCB4#9*L?j7c=x;
z-r5moCF?rnQl{!Ft%y^duQ>ROp3G&<mF#*qFU#l(tG_a<&mAA$(Avxjjy^W0Tj%&i
s)eow7&i@ppH$PK<(VG0c#jn@L-JE94Tx*gi4RjTQr>mdKI;Vst0J&U3*8l(j

literal 0
HcmV?d00001

diff --git a/phpBB/styles/prosilver/template/icons/svg.html b/phpBB/styles/prosilver/template/icons/svg.html
new file mode 100644
index 0000000000..67339b6ea2
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg.html
@@ -0,0 +1,21 @@
+{% spaceless %}
+	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '-'}) ~ '-' ~ random() %}
+<svg
+	class="icon svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	viewBox="0 0 24 24"
+	{% if TITLE %}
+		{% if S_HIDDEN %}aria-hidden="true"{% endif %}
+		aria-labelledby="{{ TITLE_ID }}"
+	{% endif %}
+	role="img"
+	{{ ATTRIBUTES }}
+>
+	{% if TITLE %}
+	<title id="{{ TITLE_ID }}">
+		{{ lang(TITLE) }}
+	</title>
+	{% endif %}
+
+	{{ SOURCE }}
+</svg>
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/svg/bars.svg b/phpBB/styles/prosilver/template/icons/svg/bars.svg
new file mode 100644
index 0000000000..c3317edaf5
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg/bars.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="red" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/envelope.svg b/phpBB/styles/prosilver/template/icons/svg/envelope.svg
new file mode 100644
index 0000000000..4f073b3362
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg/envelope.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></svg>
\ No newline at end of file
diff --git a/phpBB/styles/prosilver/template/icons/svg/pencil.svg b/phpBB/styles/prosilver/template/icons/svg/pencil.svg
new file mode 100644
index 0000000000..c9c021d811
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg/pencil.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>My fake title!</title><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/phone.svg b/phpBB/styles/prosilver/template/icons/svg/phone.svg
new file mode 100644
index 0000000000..c542ecee29
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg/phone.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>
\ No newline at end of file
diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 94d069b597..3e37fb788e 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -34,6 +34,36 @@
 	</form>
 <!-- ENDIF -->
 
+<div class="panel">
+	<h3>Font</h3>
+	<div>
+		{{ icon('font', 'bars', 'fa-fw icon-blue') }}
+		{{ icon('font', 'envelope-o') }}
+		{{ icon('font', 'pencil', '', '', true, {'data-ajax': 'true', 'data-refresh': 'true'}) }}
+		{{ icon('font', 'phone') }}
+		<a>{{ icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		<a class="button">{{ icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+	</div>
+	<h3>PNG</h3>
+	<div>
+		{{ icon('png', 'bars') }}
+		{{ icon('png', 'envelope') }}
+		{{ icon('png', 'pencil') }}
+		{{ icon('png', 'phone') }}
+		<a>{{ icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		<a class="button">{{ icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+	</div>
+	<h3>SVG</h3>
+	<div>
+		{{ icon('svg', 'bars', 'fa-fw icon-blue') }}
+		{{ icon('svg', 'envelope', '', 'USERNAME', false) }}
+		{{ icon('svg', 'pencil') }}
+		{{ icon('svg', 'phone') }}
+		<a>{{ icon('svg', 'pencil') }}</a>
+		<a class="button">{{ icon('svg', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+	</div>
+</div>
+
 <!-- EVENT index_body_stat_blocks_before -->
 
 <!-- IF S_DISPLAY_ONLINE_LIST -->
@@ -41,7 +71,7 @@
 		<!-- IF U_VIEWONLINE --><h3><a href="{U_VIEWONLINE}">{L_WHO_IS_ONLINE}</a></h3><!-- ELSE --><h3>{L_WHO_IS_ONLINE}</h3><!-- ENDIF -->
 		<p>
 			<!-- EVENT index_body_block_online_prepend -->
-			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br /> 
+			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br />
 			<!-- IF U_VIEWONLINE -->
 				<br />{LOGGED_IN_USER_LIST}
 				<!-- IF LEGEND --><br /><em>{L_LEGEND}{L_COLON} {LEGEND}</em><!-- ENDIF -->
diff --git a/phpBB/styles/prosilver/theme/icons.css b/phpBB/styles/prosilver/theme/icons.css
index 1268627d51..5a2e288e3d 100644
--- a/phpBB/styles/prosilver/theme/icons.css
+++ b/phpBB/styles/prosilver/theme/icons.css
@@ -5,6 +5,11 @@
 /* Global module setup
 ---------------------------------------- */
 
+/* Global svg colours fix */
+svg {
+	fill: currentColor;
+}
+
 /* Renamed version of .fa class for agnostic usage of icon fonts.
  * Just change the name of the font after the 14/1 to the name of
  * the font you wish to use.
@@ -27,6 +32,14 @@ blockquote cite:before,
 	text-rendering: auto; /* optimizelegibility throws things off #1094 */
 }
 
+img.icon,
+svg.icon {
+	width: 14px;
+	height: 14px;
+	display: inline-block;
+	vertical-align: middle;
+}
+
 .icon:before {
 	padding-right: 2px;
 }
@@ -39,18 +52,26 @@ blockquote cite:before,
 
 .icon.icon-xl {
 	font-size: 20px;
+	width: 20px;
+	height: 20px;
 }
 
 .icon.icon-lg {
 	font-size: 16px;
+	width: 16px;
+	height: 16px;
 }
 
 .icon.icon-md {
 	font-size: 10px;
+	width: 10px;
+	height: 10px;
 }
 
 .icon.icon-sm {
 	font-size: 8px;
+	width: 8px;
+	height: 8px;
 }
 
 /* icon modifiers */

From 2f4aef8689ddc38c90ac0e251e01fcb321dcd338 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Fri, 8 Mar 2019 22:48:36 +0100
Subject: [PATCH 02/25] [ticket/15538] Fix end of files

PHPBB3-15538
---
 phpBB/styles/prosilver/template/icons/svg/envelope.svg | 2 +-
 phpBB/styles/prosilver/template/icons/svg/phone.svg    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/phpBB/styles/prosilver/template/icons/svg/envelope.svg b/phpBB/styles/prosilver/template/icons/svg/envelope.svg
index 4f073b3362..013006c894 100644
--- a/phpBB/styles/prosilver/template/icons/svg/envelope.svg
+++ b/phpBB/styles/prosilver/template/icons/svg/envelope.svg
@@ -1 +1 @@
-<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></svg>
\ No newline at end of file
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/phone.svg b/phpBB/styles/prosilver/template/icons/svg/phone.svg
index c542ecee29..5fbfe196ba 100644
--- a/phpBB/styles/prosilver/template/icons/svg/phone.svg
+++ b/phpBB/styles/prosilver/template/icons/svg/phone.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>

From 5f62976d66c1ba693378c169650ed67b78f29851 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Sun, 10 Mar 2019 18:42:31 +0100
Subject: [PATCH 03/25] [ticket/15538] o-icon class and stylelint

PHPBB3-15538
---
 phpBB/styles/prosilver/template/icons/font.html |  2 +-
 phpBB/styles/prosilver/template/icons/png.html  |  2 +-
 phpBB/styles/prosilver/template/icons/svg.html  |  3 ++-
 phpBB/styles/prosilver/theme/icons.css          | 15 ++++++++++-----
 4 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/phpBB/styles/prosilver/template/icons/font.html b/phpBB/styles/prosilver/template/icons/font.html
index 488d434fa9..21b211a234 100644
--- a/phpBB/styles/prosilver/template/icons/font.html
+++ b/phpBB/styles/prosilver/template/icons/font.html
@@ -1,6 +1,6 @@
 {% spaceless %}
 <i
-	class="icon fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	class="o-icon font fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
 	{% if S_HIDDEN %}
 		{% if TITLE %}title="{{ lang(TITLE) }}"{% endif %}
 		aria-hidden="true"
diff --git a/phpBB/styles/prosilver/template/icons/png.html b/phpBB/styles/prosilver/template/icons/png.html
index dafdece71e..f20a88fe6d 100644
--- a/phpBB/styles/prosilver/template/icons/png.html
+++ b/phpBB/styles/prosilver/template/icons/png.html
@@ -1,6 +1,6 @@
 {% spaceless %}
 <img
-	class="icon png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	class="o-icon png png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
 	src="{{ SOURCE }}"
 	alt="{{ lang(TITLE) }}"
 	{{ ATTRIBUTES }}
diff --git a/phpBB/styles/prosilver/template/icons/svg.html b/phpBB/styles/prosilver/template/icons/svg.html
index 67339b6ea2..c12aa8fe63 100644
--- a/phpBB/styles/prosilver/template/icons/svg.html
+++ b/phpBB/styles/prosilver/template/icons/svg.html
@@ -1,7 +1,8 @@
 {% spaceless %}
 	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '-'}) ~ '-' ~ random() %}
 <svg
-	class="icon svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	class="o-icon svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
+	xmlns="http://www.w3.org/2000/svg"
 	viewBox="0 0 24 24"
 	{% if TITLE %}
 		{% if S_HIDDEN %}aria-hidden="true"{% endif %}
diff --git a/phpBB/styles/prosilver/theme/icons.css b/phpBB/styles/prosilver/theme/icons.css
index 5a2e288e3d..2338310b9b 100644
--- a/phpBB/styles/prosilver/theme/icons.css
+++ b/phpBB/styles/prosilver/theme/icons.css
@@ -14,6 +14,7 @@ svg {
  * Just change the name of the font after the 14/1 to the name of
  * the font you wish to use.
  */
+.o-icon,
 .icon,
 .button .icon,
 blockquote cite:before,
@@ -32,42 +33,46 @@ blockquote cite:before,
 	text-rendering: auto; /* optimizelegibility throws things off #1094 */
 }
 
-img.icon,
-svg.icon {
+.o-icon.png,
+.o-icon.svg {
+	vertical-align: middle;
 	width: 14px;
 	height: 14px;
-	display: inline-block;
-	vertical-align: middle;
 }
 
+.o-icon:before,
 .icon:before {
 	padding-right: 2px;
 }
 
+.button .o-icon:before,
 .button .icon:before {
 	padding-right: 0;
 }
 
 /* Icon size classes - Default size is 14px, use these for small variations */
-
+.o-icon.icon-xl,
 .icon.icon-xl {
 	font-size: 20px;
 	width: 20px;
 	height: 20px;
 }
 
+.o-icon.icon-lg,
 .icon.icon-lg {
 	font-size: 16px;
 	width: 16px;
 	height: 16px;
 }
 
+.o-icon.icon-md,
 .icon.icon-md {
 	font-size: 10px;
 	width: 10px;
 	height: 10px;
 }
 
+.o-icon.icon-sm,
 .icon.icon-sm {
 	font-size: 8px;
 	width: 8px;

From 094f0f663caae9b8639f9a6ffcb08c9891bd0d72 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Wed, 13 Mar 2019 15:02:43 +0100
Subject: [PATCH 04/25] [ticket/15538] Capitalize function

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php  |  2 +-
 .../styles/prosilver/template/index_body.html | 36 +++++++++----------
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index be83222a62..3611f19042 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -48,7 +48,7 @@ class icon extends \Twig_Extension
 	public function getFunctions()
 	{
 		return [
-			new \Twig_SimpleFunction('icon', [$this, 'icon'], ['needs_environment' => true]),
+			new \Twig_SimpleFunction('Icon', [$this, 'icon'], ['needs_environment' => true]),
 		];
 	}
 
diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 3e37fb788e..38248328f0 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -37,30 +37,30 @@
 <div class="panel">
 	<h3>Font</h3>
 	<div>
-		{{ icon('font', 'bars', 'fa-fw icon-blue') }}
-		{{ icon('font', 'envelope-o') }}
-		{{ icon('font', 'pencil', '', '', true, {'data-ajax': 'true', 'data-refresh': 'true'}) }}
-		{{ icon('font', 'phone') }}
-		<a>{{ icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-		<a class="button">{{ icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		{{ Icon('font', 'bars', 'fa-fw icon-blue') }}
+		{{ Icon('font', 'envelope-o') }}
+		{{ Icon('font', 'pencil', '', '', true, {'data-ajax': 'true', 'data-refresh': 'true'}) }}
+		{{ Icon('font', 'phone') }}
+		<a>{{ Icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		<a class="button">{{ Icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
 	</div>
 	<h3>PNG</h3>
 	<div>
-		{{ icon('png', 'bars') }}
-		{{ icon('png', 'envelope') }}
-		{{ icon('png', 'pencil') }}
-		{{ icon('png', 'phone') }}
-		<a>{{ icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-		<a class="button">{{ icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		{{ Icon('png', 'bars') }}
+		{{ Icon('png', 'envelope') }}
+		{{ Icon('png', 'pencil') }}
+		{{ Icon('png', 'phone') }}
+		<a>{{ Icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		<a class="button">{{ Icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
 	</div>
 	<h3>SVG</h3>
 	<div>
-		{{ icon('svg', 'bars', 'fa-fw icon-blue') }}
-		{{ icon('svg', 'envelope', '', 'USERNAME', false) }}
-		{{ icon('svg', 'pencil') }}
-		{{ icon('svg', 'phone') }}
-		<a>{{ icon('svg', 'pencil') }}</a>
-		<a class="button">{{ icon('svg', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
+		{{ Icon('svg', 'bars', 'fa-fw icon-blue') }}
+		{{ Icon('svg', 'envelope', '', 'USERNAME', false) }}
+		{{ Icon('svg', 'pencil') }}
+		{{ Icon('svg', 'phone') }}
+		<a>{{ Icon('svg', 'pencil') }}</a>
+		<a class="button">{{ Icon('svg', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
 	</div>
 </div>
 

From d8274c19c8565f6980c33bded82b5f0683e2af85 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 30 Sep 2019 16:42:03 +0200
Subject: [PATCH 05/25] [ticket/15538] Create macro and remove icons

PHPBB3-15538
---
 .../default/container/services_twig.yml       |   1 +
 phpBB/phpbb/template/twig/extension/icon.php  | 299 ++++++++++--------
 phpBB/styles/all/template/macros.html         |  33 ++
 .../styles/prosilver/template/icons/font.html |  11 -
 .../styles/prosilver/template/icons/png.html  |   8 -
 .../prosilver/template/icons/png/bars.png     | Bin 85 -> 0 bytes
 .../prosilver/template/icons/png/envelope.png | Bin 189 -> 0 bytes
 .../prosilver/template/icons/png/pencil.png   | Bin 165 -> 0 bytes
 .../prosilver/template/icons/png/phone.png    | Bin 195 -> 0 bytes
 .../styles/prosilver/template/icons/svg.html  |  22 --
 .../prosilver/template/icons/svg/bars.svg     |   1 -
 .../prosilver/template/icons/svg/envelope.svg |   1 -
 .../prosilver/template/icons/svg/pencil.svg   |   1 -
 .../prosilver/template/icons/svg/phone.svg    |   1 -
 14 files changed, 200 insertions(+), 178 deletions(-)
 create mode 100644 phpBB/styles/all/template/macros.html
 delete mode 100644 phpBB/styles/prosilver/template/icons/font.html
 delete mode 100644 phpBB/styles/prosilver/template/icons/png.html
 delete mode 100644 phpBB/styles/prosilver/template/icons/png/bars.png
 delete mode 100644 phpBB/styles/prosilver/template/icons/png/envelope.png
 delete mode 100644 phpBB/styles/prosilver/template/icons/png/pencil.png
 delete mode 100644 phpBB/styles/prosilver/template/icons/png/phone.png
 delete mode 100644 phpBB/styles/prosilver/template/icons/svg.html
 delete mode 100644 phpBB/styles/prosilver/template/icons/svg/bars.svg
 delete mode 100644 phpBB/styles/prosilver/template/icons/svg/envelope.svg
 delete mode 100644 phpBB/styles/prosilver/template/icons/svg/pencil.svg
 delete mode 100644 phpBB/styles/prosilver/template/icons/svg/phone.svg

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 @@
 <?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.
-*
-*/
+ *
+ * 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
+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 <html> 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 = {}) %}
+	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ _self.attributes(attributes) }}></i>
+	{% if title %}<span{% if hidden %} class="sr-only"{% endif %}>{{ lang(title) }}</span>{% endif %}
+{% endmacro Fa %}
+
+{# PNG icons #}
+{% macro Png(icon, classes = '', title = '', hidden = false, attributes = {}) %}
+	<img class="o-icon png png-{{ icon ~ Implode_classes(classes) }}" src="{{ icon|png_path }}" alt="{{ lang(title) }}"{{ _self.attributes(attributes) }}
+{% endmacro Png %}
+
+{# SVG icons #}
+{% macro Svg(icon, classes = '', title = '', hidden = false, attributes = {}) %}
+	{% set title_id = title ? title|lower|replace({' ': '_'}) ~ '-' ~ random() %}
+
+	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ _self.attributes(attributes) }}>
+	{% if title %}
+		<title id="{{ title_id }}">{{ lang(title) }}</title>
+	{% endif %}
+
+		{{ svg_clean(icon) }}
+	</svg>
+{% endmacro Svg %}
diff --git a/phpBB/styles/prosilver/template/icons/font.html b/phpBB/styles/prosilver/template/icons/font.html
deleted file mode 100644
index 21b211a234..0000000000
--- a/phpBB/styles/prosilver/template/icons/font.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% spaceless %}
-<i
-	class="o-icon font fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
-	{% if S_HIDDEN %}
-		{% if TITLE %}title="{{ lang(TITLE) }}"{% endif %}
-		aria-hidden="true"
-	{% endif %}
-	{{ ATTRIBUTES }}
-></i>
-{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
-{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/png.html b/phpBB/styles/prosilver/template/icons/png.html
deleted file mode 100644
index f20a88fe6d..0000000000
--- a/phpBB/styles/prosilver/template/icons/png.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% spaceless %}
-<img
-	class="o-icon png png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
-	src="{{ SOURCE }}"
-	alt="{{ lang(TITLE) }}"
-	{{ ATTRIBUTES }}
-/>
-{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/png/bars.png b/phpBB/styles/prosilver/template/icons/png/bars.png
deleted file mode 100644
index e8dcba56ec85c8e45837462b1a4702601384ca8d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 85
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|8BZ6-kP60R1<szP|4$NqE;xuS
i)0W^}e9=IPmEn!L-SrhZ1qXrZ7(8A5T-G@yGywnwjurR-

diff --git a/phpBB/styles/prosilver/template/icons/png/envelope.png b/phpBB/styles/prosilver/template/icons/png/envelope.png
deleted file mode 100644
index d435a9b648c8da1c64eff9788c39a63b507e361c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 189
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mT0C7GLn;{eUf9Un6d=&>P=DGn
zPM$Rfq)d2P%ocIKP@84I=^WtTz1nhj_=@$$ckb7fAC0W{+I+%9yD{uVsr{=~<^rL4
zdD|Egp1)Ep5z6XuGi|t)am8bWaOUU4^#%UZ?FB;Ex{oekQYjH*FpFBt`$X`8bnL&j
nKBwuLwwnz)eI{SJ^_lI0RQUeoy5iA5_b_<6`njxgN@xNAZSX~h

diff --git a/phpBB/styles/prosilver/template/icons/png/pencil.png b/phpBB/styles/prosilver/template/icons/png/pencil.png
deleted file mode 100644
index 9ec4db8f9dbc646713f4f18a379b0cbcccb0c0c8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 165
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|may(reLn;{eo>CNYaFA%YxPIlN
zWmYXqG`l1n3)OpX7YLnY6j9c(->iHqHu=M+$Lx~4^Y)6Y;h4MjCkqdUQ?{0&u%)7M
z*Oibo7FSP~(paHbm+coqrftrdqAyaq{7FCemv==f<&D2GY+2{uW{i-xJ{9}T-V<mq
NgQu&X%Q~loCICznI8p!r

diff --git a/phpBB/styles/prosilver/template/icons/png/phone.png b/phpBB/styles/prosilver/template/icons/png/phone.png
deleted file mode 100644
index 7a7e756311d53eb3be3eb55b2ec236b7a991379f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 195
zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mx;$MRLn;{8o@!)u3S@A&IN8Kc
z=^~4+AR`N>qv8~)g$qO+xJ4FxiC!eY!N|0rL;PpcC%Y2817Fj9JCB4#9*L?j7c=x;
z-r5moCF?rnQl{!Ft%y^duQ>ROp3G&<mF#*qFU#l(tG_a<&mAA$(Avxjjy^W0Tj%&i
s)eow7&i@ppH$PK<(VG0c#jn@L-JE94Tx*gi4RjTQr>mdKI;Vst0J&U3*8l(j

diff --git a/phpBB/styles/prosilver/template/icons/svg.html b/phpBB/styles/prosilver/template/icons/svg.html
deleted file mode 100644
index c12aa8fe63..0000000000
--- a/phpBB/styles/prosilver/template/icons/svg.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% spaceless %}
-	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '-'}) ~ '-' ~ random() %}
-<svg
-	class="o-icon svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"
-	xmlns="http://www.w3.org/2000/svg"
-	viewBox="0 0 24 24"
-	{% if TITLE %}
-		{% if S_HIDDEN %}aria-hidden="true"{% endif %}
-		aria-labelledby="{{ TITLE_ID }}"
-	{% endif %}
-	role="img"
-	{{ ATTRIBUTES }}
->
-	{% if TITLE %}
-	<title id="{{ TITLE_ID }}">
-		{{ lang(TITLE) }}
-	</title>
-	{% endif %}
-
-	{{ SOURCE }}
-</svg>
-{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/svg/bars.svg b/phpBB/styles/prosilver/template/icons/svg/bars.svg
deleted file mode 100644
index c3317edaf5..0000000000
--- a/phpBB/styles/prosilver/template/icons/svg/bars.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="red" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/envelope.svg b/phpBB/styles/prosilver/template/icons/svg/envelope.svg
deleted file mode 100644
index 013006c894..0000000000
--- a/phpBB/styles/prosilver/template/icons/svg/envelope.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 14H4V8l8 5 8-5v10zm-8-7L4 6h16l-8 5z"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/pencil.svg b/phpBB/styles/prosilver/template/icons/svg/pencil.svg
deleted file mode 100644
index c9c021d811..0000000000
--- a/phpBB/styles/prosilver/template/icons/svg/pencil.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>My fake title!</title><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
diff --git a/phpBB/styles/prosilver/template/icons/svg/phone.svg b/phpBB/styles/prosilver/template/icons/svg/phone.svg
deleted file mode 100644
index 5fbfe196ba..0000000000
--- a/phpBB/styles/prosilver/template/icons/svg/phone.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>

From a10087a7173c76281eea71e9370351315bfc7d81 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 30 Sep 2019 16:45:50 +0200
Subject: [PATCH 06/25] [ticket/15538] Uncommit test files

PHPBB3-15538
---
 .../styles/prosilver/template/index_body.html | 32 +------------------
 phpBB/styles/prosilver/theme/icons.css        | 28 +---------------
 2 files changed, 2 insertions(+), 58 deletions(-)

diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 38248328f0..94d069b597 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -34,36 +34,6 @@
 	</form>
 <!-- ENDIF -->
 
-<div class="panel">
-	<h3>Font</h3>
-	<div>
-		{{ Icon('font', 'bars', 'fa-fw icon-blue') }}
-		{{ Icon('font', 'envelope-o') }}
-		{{ Icon('font', 'pencil', '', '', true, {'data-ajax': 'true', 'data-refresh': 'true'}) }}
-		{{ Icon('font', 'phone') }}
-		<a>{{ Icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-		<a class="button">{{ Icon('font', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-	</div>
-	<h3>PNG</h3>
-	<div>
-		{{ Icon('png', 'bars') }}
-		{{ Icon('png', 'envelope') }}
-		{{ Icon('png', 'pencil') }}
-		{{ Icon('png', 'phone') }}
-		<a>{{ Icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-		<a class="button">{{ Icon('png', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-	</div>
-	<h3>SVG</h3>
-	<div>
-		{{ Icon('svg', 'bars', 'fa-fw icon-blue') }}
-		{{ Icon('svg', 'envelope', '', 'USERNAME', false) }}
-		{{ Icon('svg', 'pencil') }}
-		{{ Icon('svg', 'phone') }}
-		<a>{{ Icon('svg', 'pencil') }}</a>
-		<a class="button">{{ Icon('svg', 'pencil', 'fa-fw', 'POST_REPLY') }}</a>
-	</div>
-</div>
-
 <!-- EVENT index_body_stat_blocks_before -->
 
 <!-- IF S_DISPLAY_ONLINE_LIST -->
@@ -71,7 +41,7 @@
 		<!-- IF U_VIEWONLINE --><h3><a href="{U_VIEWONLINE}">{L_WHO_IS_ONLINE}</a></h3><!-- ELSE --><h3>{L_WHO_IS_ONLINE}</h3><!-- ENDIF -->
 		<p>
 			<!-- EVENT index_body_block_online_prepend -->
-			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br />
+			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br /> 
 			<!-- IF U_VIEWONLINE -->
 				<br />{LOGGED_IN_USER_LIST}
 				<!-- IF LEGEND --><br /><em>{L_LEGEND}{L_COLON} {LEGEND}</em><!-- ENDIF -->
diff --git a/phpBB/styles/prosilver/theme/icons.css b/phpBB/styles/prosilver/theme/icons.css
index 2338310b9b..1268627d51 100644
--- a/phpBB/styles/prosilver/theme/icons.css
+++ b/phpBB/styles/prosilver/theme/icons.css
@@ -5,16 +5,10 @@
 /* Global module setup
 ---------------------------------------- */
 
-/* Global svg colours fix */
-svg {
-	fill: currentColor;
-}
-
 /* Renamed version of .fa class for agnostic usage of icon fonts.
  * Just change the name of the font after the 14/1 to the name of
  * the font you wish to use.
  */
-.o-icon,
 .icon,
 .button .icon,
 blockquote cite:before,
@@ -33,50 +27,30 @@ blockquote cite:before,
 	text-rendering: auto; /* optimizelegibility throws things off #1094 */
 }
 
-.o-icon.png,
-.o-icon.svg {
-	vertical-align: middle;
-	width: 14px;
-	height: 14px;
-}
-
-.o-icon:before,
 .icon:before {
 	padding-right: 2px;
 }
 
-.button .o-icon:before,
 .button .icon:before {
 	padding-right: 0;
 }
 
 /* Icon size classes - Default size is 14px, use these for small variations */
-.o-icon.icon-xl,
+
 .icon.icon-xl {
 	font-size: 20px;
-	width: 20px;
-	height: 20px;
 }
 
-.o-icon.icon-lg,
 .icon.icon-lg {
 	font-size: 16px;
-	width: 16px;
-	height: 16px;
 }
 
-.o-icon.icon-md,
 .icon.icon-md {
 	font-size: 10px;
-	width: 10px;
-	height: 10px;
 }
 
-.o-icon.icon-sm,
 .icon.icon-sm {
 	font-size: 8px;
-	width: 8px;
-	height: 8px;
 }
 
 /* icon modifiers */

From 971b905569231927235949f5ff89cbd5e97db578 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 30 Sep 2019 17:26:46 +0200
Subject: [PATCH 07/25] [ticket/15538] Split macros and add docblocks

PHPBB3-15538
---
 .../default/container/services_twig.yml       |   9 +-
 phpBB/phpbb/template/twig/extension/icon.php  | 145 ++++-----------
 .../phpbb/template/twig/extension/implode.php | 175 ++++++++++++++++++
 phpBB/phpbb/template/twig/extension/macro.php |  64 +++++++
 phpBB/styles/all/template/macros.html         |   4 +-
 5 files changed, 280 insertions(+), 117 deletions(-)
 create mode 100644 phpBB/phpbb/template/twig/extension/implode.php
 create mode 100644 phpBB/phpbb/template/twig/extension/macro.php

diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml
index 8c75d792b1..858a3a5e75 100644
--- a/phpBB/config/default/container/services_twig.yml
+++ b/phpBB/config/default/container/services_twig.yml
@@ -57,11 +57,18 @@ services:
     template.twig.extensions.icon:
         class: phpbb\template\twig\extension\icon
         arguments:
-            - '@template.twig.environment'
             - '@user'
         tags:
             - { name: twig.extension }
 
+    template.twig.extensions.implode:
+        class: phpbb\template\twig\extension\implode
+
+    template.twig.extensions.macro:
+        class: phpbb\template\twig\extension\macro
+        arguments:
+            - '@template.twig.environment'
+
     template.twig.extensions.routing:
         class: phpbb\template\twig\extension\routing
         arguments:
diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index dee48908ee..f650438d66 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -15,21 +15,25 @@ namespace phpbb\template\twig\extension;
 
 use phpbb\template\twig\environment;
 
-abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
+abstract class icon extends \Twig\Extension\AbstractExtension
 {
-	protected $twig;
+	/** @var \phpbb\user */
 	protected $user;
 
-	public function __construct(environment $twig, \phpbb\user $user)
+	/**
+	 * Constructor.
+	 *
+	 * @param \phpbb\user	$user		User object
+	 */
+	public function __construct(\phpbb\user $user)
 	{
-		$this->twig = $twig;
 		$this->user = $user;
 	}
 
 	/**
-	 * Get the name of this extension.
+	 * Returns the name of this extension.
 	 *
-	 * @return string
+	 * @return string The extension name
 	 */
 	public function getName()
 	{
@@ -37,27 +41,10 @@ abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\E
 	}
 
 	/**
-	 * Returns a list of global variables to add to the existing list.
+	 * Returns a list of filters to add to the existing list.
 	 *
-	 * @return array An array of global variables
+	 * @return \Twig\TwigFilter[]
 	 */
-	public function getGlobals()
-	{
-		$macros = null;
-
-		try
-		{
-			$macros = $this->twig->loadTemplate('macros.html');
-		}
-		catch (\Twig\Error\Error $e)
-		{
-		}
-
-		return [
-			'macro'	=> $macros,
-		];
-	}
-
 	public function getFilters()
 	{
 		return [
@@ -65,15 +52,25 @@ abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\E
 		];
 	}
 
+	/**
+	 * Returns a list of functions to add to the existing list.
+	 *
+	 * @return \Twig\TwigFunction[]
+	 */
 	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']),
 		];
 	}
 
+	/**
+	 * Create a path to a PNG template icon.
+	 *
+	 * @param environment	$environment	Twig environment object
+	 * @param string		$icon			The icon name
+	 * @return string
+	 */
 	protected function png_path(environment $environment, $icon)
 	{
 		$board_url	= defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH;
@@ -83,6 +80,13 @@ abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\E
 		return "{$web_path}styles/{$style_path}/template/icons/png/{$icon}.png";
 	}
 
+	/**
+	 * Load and clean an SVG template icon.
+	 *
+	 * @param environment	$environment	Twig environment object
+	 * @param string		$icon			The icon name
+	 * @return string
+	 */
 	protected function svg_clean(environment $environment, $icon)
 	{
 		try
@@ -98,7 +102,6 @@ abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\E
 		$svg = $src->getCode();
 
 		$doc = new \DOMDocument();
-		$doc->formatOutput = false;
 		$doc->preserveWhiteSpace = false;
 		$doc->strictErrorChecking = false;
 
@@ -142,90 +145,4 @@ abstract class icon extends \Twig\Extension\AbstractExtension implements \Twig\E
 
 		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;
-					}
-				}
-			}
-		}
-
-		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/phpbb/template/twig/extension/implode.php b/phpBB/phpbb/template/twig/extension/implode.php
new file mode 100644
index 0000000000..6d0ef58b3f
--- /dev/null
+++ b/phpBB/phpbb/template/twig/extension/implode.php
@@ -0,0 +1,175 @@
+<?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;
+
+abstract class implode extends \Twig\Extension\AbstractExtension
+{
+	/**
+	 * Returns the name of this extension.
+	 *
+	 * @return string The extension name
+	 */
+	public function getName()
+	{
+		return 'implode';
+	}
+
+	/**
+	 * Returns a list of functions to add to the existing list.
+	 *
+	 * @return \Twig\TwigFunction[]
+	 */
+	public function getFunctions()
+	{
+		return [
+			new \Twig\TwigFunction('Implode_attributes', [$this, 'implode_attributes']),
+			new \Twig\TwigFunction('Implode_classes', [$this, 'implode_classes']),
+		];
+	}
+
+	/**
+	 * Implode an array of attributes to a string.
+	 *
+	 * This string will be prepended by a space for ease-of-use.
+	 *
+	 * Examples would be:
+	 * Implode_attributes('checked', {'data-ajax': 'true'})
+	 * Implode_attributes(['checked', {'data-ajax': 'true'}])
+	 *
+	 * @param mixed		$arguments
+	 * @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 if (is_array($value))
+					{
+						if (is_integer($key) && is_string($value))
+						{
+							$attributes[] = $value;
+						}
+						else
+						{
+							$attributes[$key] = $value;
+						}
+					}
+					else
+					{
+						$attributes[$key] = $value;
+					}
+				}
+			}
+		}
+
+		foreach ($attributes as $attribute => $value)
+		{
+			if (is_string($attribute))
+			{
+				$string .= ' ' . $attribute . '="' . $value . '"';
+			}
+			else
+			{
+				$string .= ' ' . $attribute;
+			}
+		}
+
+		return $string;
+	}
+
+	/**
+	 * Implode an array or classes to a string.
+	 *
+	 * This string will be prepended with a space for ease-of-use.
+	 *
+	 * Conditions can be added to the classes, which will determine if the classes is added to the string.
+	 * @see https://twig.symfony.com/doc/2.x/functions/html_classes.html
+	 *
+	 * An example would be:
+	 * Implode_classes('a-class', 'another-class', {
+	 * 		'reported-class': S_POST_REPORTED,
+	 * 		'hidden-class': S_POST_HIDDEN,
+	 * })
+	 *
+	 * This function differs from the html_classes function linked above,
+	 * in that it allows another depth level, so it also supports a single argument.
+	 *
+	 * An example would be:
+	 * Implode_classes(['a-class', 'another-class', {
+	 * 		'reported-class': S_POST_REPORTED,
+	 * 		'hidden-class': S_POST_HIDDEN,
+	 * }])
+	 *
+	 * @param mixed		$arguments
+	 * @return string					The classes string prepended with a space
+	 */
+	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/phpbb/template/twig/extension/macro.php b/phpBB/phpbb/template/twig/extension/macro.php
new file mode 100644
index 0000000000..fa7614506a
--- /dev/null
+++ b/phpBB/phpbb/template/twig/extension/macro.php
@@ -0,0 +1,64 @@
+<?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;
+
+abstract class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
+{
+	/** @var environment */
+	protected $twig;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param environment	$twig		Twig environment object
+	 */
+	public function __construct(environment $twig)
+	{
+		$this->twig = $twig;
+	}
+
+	/**
+	 * Returns the name of this extension.
+	 *
+	 * @return string The extension name
+	 */
+	public function getName()
+	{
+		return 'macro';
+	}
+
+	/**
+	 * Returns a list of global variables to add to the existing list.
+	 *
+	 * @return array An array of global variables
+	 */
+	public function getGlobals()
+	{
+		$macros = null;
+
+		try
+		{
+			$macros = $this->twig->loadTemplate('macros.html');
+		}
+		catch (\Twig\Error\Error $e)
+		{
+		}
+
+		return [
+			'macros' => $macros,
+		];
+	}
+}
diff --git a/phpBB/styles/all/template/macros.html b/phpBB/styles/all/template/macros.html
index aaa9990763..5a62d5374e 100644
--- a/phpBB/styles/all/template/macros.html
+++ b/phpBB/styles/all/template/macros.html
@@ -10,7 +10,7 @@
 
 {# FA icons #}
 {% macro Fa(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ _self.attributes(attributes) }}></i>
+	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ Implode_attributes(attributes) }}></i>
 	{% if title %}<span{% if hidden %} class="sr-only"{% endif %}>{{ lang(title) }}</span>{% endif %}
 {% endmacro Fa %}
 
@@ -23,7 +23,7 @@
 {% macro Svg(icon, classes = '', title = '', hidden = false, attributes = {}) %}
 	{% set title_id = title ? title|lower|replace({' ': '_'}) ~ '-' ~ random() %}
 
-	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ _self.attributes(attributes) }}>
+	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ Implode_attributes(attributes) }}>
 	{% if title %}
 		<title id="{{ title_id }}">{{ lang(title) }}</title>
 	{% endif %}

From 23fae74bf23e6c1d9b154e5e51a8284d7e02da8b Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 30 Sep 2019 18:51:37 +0200
Subject: [PATCH 08/25] [ticket/15538] Fixes unrelated to the error

PHPBB3-15538
---
 .../default/container/services_twig.yml       |  4 +++
 phpBB/phpbb/template/twig/extension/icon.php  | 14 +++++----
 .../phpbb/template/twig/extension/implode.php | 26 +++++++++-------
 phpBB/phpbb/template/twig/extension/macro.php |  4 +--
 phpBB/styles/all/template/macros.html         | 30 +------------------
 .../styles/prosilver/template/index_body.html |  4 ++-
 6 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml
index 858a3a5e75..9d9436f89e 100644
--- a/phpBB/config/default/container/services_twig.yml
+++ b/phpBB/config/default/container/services_twig.yml
@@ -63,11 +63,15 @@ services:
 
     template.twig.extensions.implode:
         class: phpbb\template\twig\extension\implode
+        tags:
+            - { name: twig.extension }
 
     template.twig.extensions.macro:
         class: phpbb\template\twig\extension\macro
         arguments:
             - '@template.twig.environment'
+        tags:
+            - { name: twig.extension }
 
     template.twig.extensions.routing:
         class: phpbb\template\twig\extension\routing
diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index f650438d66..1a6adc48f3 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -15,7 +15,7 @@ namespace phpbb\template\twig\extension;
 
 use phpbb\template\twig\environment;
 
-abstract class icon extends \Twig\Extension\AbstractExtension
+class icon extends \Twig\Extension\AbstractExtension
 {
 	/** @var \phpbb\user */
 	protected $user;
@@ -48,7 +48,7 @@ abstract class icon extends \Twig\Extension\AbstractExtension
 	public function getFilters()
 	{
 		return [
-			new \Twig\TwigFilter('Png_path', [$this, 'png_path'], ['needs_environment' => true]),
+			new \Twig\TwigFilter('png_path', [$this, 'png_path'], ['needs_environment' => true]),
 		];
 	}
 
@@ -71,7 +71,7 @@ abstract class icon extends \Twig\Extension\AbstractExtension
 	 * @param string		$icon			The icon name
 	 * @return string
 	 */
-	protected function png_path(environment $environment, $icon)
+	public 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();
@@ -87,7 +87,7 @@ abstract class icon extends \Twig\Extension\AbstractExtension
 	 * @param string		$icon			The icon name
 	 * @return string
 	 */
-	protected function svg_clean(environment $environment, $icon)
+	public function svg_clean(environment $environment, $icon)
 	{
 		try
 		{
@@ -110,8 +110,10 @@ abstract class icon extends \Twig\Extension\AbstractExtension
 			return '';
 		}
 
-		foreach ($doc->childNodes as $child) {
-			if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
+		foreach ($doc->childNodes as $child)
+		{
+			if ($child->nodeType === XML_DOCUMENT_TYPE_NODE)
+			{
 				$child->parentNode->removeChild($child);
 			}
 		}
diff --git a/phpBB/phpbb/template/twig/extension/implode.php b/phpBB/phpbb/template/twig/extension/implode.php
index 6d0ef58b3f..5c6d313a4d 100644
--- a/phpBB/phpbb/template/twig/extension/implode.php
+++ b/phpBB/phpbb/template/twig/extension/implode.php
@@ -13,7 +13,7 @@
 
 namespace phpbb\template\twig\extension;
 
-abstract class implode extends \Twig\Extension\AbstractExtension
+class implode extends \Twig\Extension\AbstractExtension
 {
 	/**
 	 * Returns the name of this extension.
@@ -50,7 +50,7 @@ abstract class implode extends \Twig\Extension\AbstractExtension
 	 * @param mixed		$arguments
 	 * @return string
 	 */
-	protected function implode_attributes(...$arguments)
+	public function implode_attributes(...$arguments)
 	{
 		$string = '';
 		$attributes = [];
@@ -71,13 +71,17 @@ abstract class implode extends \Twig\Extension\AbstractExtension
 					}
 					else if (is_array($value))
 					{
-						if (is_integer($key) && is_string($value))
+						foreach ($value as $k => $v)
 						{
-							$attributes[] = $value;
-						}
-						else
-						{
-							$attributes[$key] = $value;
+							if (is_integer($k) && is_string($v))
+							{
+								$attributes[] = $v;
+							}
+							else
+							{
+
+								$attributes[$k] = $v;
+							}
 						}
 					}
 					else
@@ -92,11 +96,13 @@ abstract class implode extends \Twig\Extension\AbstractExtension
 		{
 			if (is_string($attribute))
 			{
+				$value = is_bool($value) ? ($value ? 'true' : 'false') : $value;
+
 				$string .= ' ' . $attribute . '="' . $value . '"';
 			}
 			else
 			{
-				$string .= ' ' . $attribute;
+				$string .= ' ' . $value;
 			}
 		}
 
@@ -129,7 +135,7 @@ abstract class implode extends \Twig\Extension\AbstractExtension
 	 * @param mixed		$arguments
 	 * @return string					The classes string prepended with a space
 	 */
-	protected function implode_classes(...$arguments)
+	public function implode_classes(...$arguments)
 	{
 		$classes = [];
 
diff --git a/phpBB/phpbb/template/twig/extension/macro.php b/phpBB/phpbb/template/twig/extension/macro.php
index fa7614506a..f786a15de1 100644
--- a/phpBB/phpbb/template/twig/extension/macro.php
+++ b/phpBB/phpbb/template/twig/extension/macro.php
@@ -15,7 +15,7 @@ namespace phpbb\template\twig\extension;
 
 use phpbb\template\twig\environment;
 
-abstract class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
+class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
 {
 	/** @var environment */
 	protected $twig;
@@ -37,7 +37,7 @@ abstract class macro extends \Twig\Extension\AbstractExtension implements \Twig\
 	 */
 	public function getName()
 	{
-		return 'macro';
+		return 'macros';
 	}
 
 	/**
diff --git a/phpBB/styles/all/template/macros.html b/phpBB/styles/all/template/macros.html
index 5a62d5374e..637140c6dc 100644
--- a/phpBB/styles/all/template/macros.html
+++ b/phpBB/styles/all/template/macros.html
@@ -1,33 +1,5 @@
 {# 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 %}
-
+Hello
 {% endmacro Icon %}
-
-{# FA icons #}
-{% macro Fa(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ Implode_attributes(attributes) }}></i>
-	{% if title %}<span{% if hidden %} class="sr-only"{% endif %}>{{ lang(title) }}</span>{% endif %}
-{% endmacro Fa %}
-
-{# PNG icons #}
-{% macro Png(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	<img class="o-icon png png-{{ icon ~ Implode_classes(classes) }}" src="{{ icon|png_path }}" alt="{{ lang(title) }}"{{ _self.attributes(attributes) }}
-{% endmacro Png %}
-
-{# SVG icons #}
-{% macro Svg(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	{% set title_id = title ? title|lower|replace({' ': '_'}) ~ '-' ~ random() %}
-
-	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ Implode_attributes(attributes) }}>
-	{% if title %}
-		<title id="{{ title_id }}">{{ lang(title) }}</title>
-	{% endif %}
-
-		{{ svg_clean(icon) }}
-	</svg>
-{% endmacro Svg %}
diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 94d069b597..4472786992 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -11,6 +11,8 @@
 <!-- ENDIF -->
 <!-- EVENT index_body_markforums_after -->
 
+Test: {{ macros.Icon('fa', 'phone') }}
+
 <!-- INCLUDE forumlist_body.html -->
 
 <!-- EVENT index_body_forumlist_body_after -->
@@ -41,7 +43,7 @@
 		<!-- IF U_VIEWONLINE --><h3><a href="{U_VIEWONLINE}">{L_WHO_IS_ONLINE}</a></h3><!-- ELSE --><h3>{L_WHO_IS_ONLINE}</h3><!-- ENDIF -->
 		<p>
 			<!-- EVENT index_body_block_online_prepend -->
-			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br /> 
+			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br />
 			<!-- IF U_VIEWONLINE -->
 				<br />{LOGGED_IN_USER_LIST}
 				<!-- IF LEGEND --><br /><em>{L_LEGEND}{L_COLON} {LEGEND}</em><!-- ENDIF -->

From 341266cc71dd745d986279add2a2de0e4497b422 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 30 Sep 2019 23:42:32 +0200
Subject: [PATCH 09/25] [ticket/15538] Readding macros and requested changes

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php  | 10 +++---
 .../phpbb/template/twig/extension/implode.php | 12 +++----
 phpBB/phpbb/template/twig/extension/macro.php | 19 +++--------
 phpBB/styles/all/template/macros.html         |  5 ---
 phpBB/styles/all/template/macros/macros.twig  | 34 +++++++++++++++++++
 5 files changed, 50 insertions(+), 30 deletions(-)
 delete mode 100644 phpBB/styles/all/template/macros.html
 create mode 100644 phpBB/styles/all/template/macros/macros.twig

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 1a6adc48f3..6c077385f3 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -23,7 +23,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	/**
 	 * Constructor.
 	 *
-	 * @param \phpbb\user	$user		User object
+	 * @param \phpbb\user	$user			User object
 	 */
 	public function __construct(\phpbb\user $user)
 	{
@@ -33,7 +33,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	/**
 	 * Returns the name of this extension.
 	 *
-	 * @return string The extension name
+	 * @return string						The extension name
 	 */
 	public function getName()
 	{
@@ -43,7 +43,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	/**
 	 * Returns a list of filters to add to the existing list.
 	 *
-	 * @return \Twig\TwigFilter[]
+	 * @return \Twig\TwigFilter[]			Array of twig filters
 	 */
 	public function getFilters()
 	{
@@ -55,7 +55,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	/**
 	 * Returns a list of functions to add to the existing list.
 	 *
-	 * @return \Twig\TwigFunction[]
+	 * @return \Twig\TwigFunction[]			Array of twig functions
 	 */
 	public function getFunctions()
 	{
@@ -77,7 +77,7 @@ class icon extends \Twig\Extension\AbstractExtension
 		$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";
+		return "{$web_path}styles/{$style_path}/theme/icons/png/{$icon}.png";
 	}
 
 	/**
diff --git a/phpBB/phpbb/template/twig/extension/implode.php b/phpBB/phpbb/template/twig/extension/implode.php
index 5c6d313a4d..93c6fe48ae 100644
--- a/phpBB/phpbb/template/twig/extension/implode.php
+++ b/phpBB/phpbb/template/twig/extension/implode.php
@@ -18,7 +18,7 @@ class implode extends \Twig\Extension\AbstractExtension
 	/**
 	 * Returns the name of this extension.
 	 *
-	 * @return string The extension name
+	 * @return string						The extension name
 	 */
 	public function getName()
 	{
@@ -28,7 +28,7 @@ class implode extends \Twig\Extension\AbstractExtension
 	/**
 	 * Returns a list of functions to add to the existing list.
 	 *
-	 * @return \Twig\TwigFunction[]
+	 * @return \Twig\TwigFunction[]			Array of twig functions
 	 */
 	public function getFunctions()
 	{
@@ -47,8 +47,8 @@ class implode extends \Twig\Extension\AbstractExtension
 	 * Implode_attributes('checked', {'data-ajax': 'true'})
 	 * Implode_attributes(['checked', {'data-ajax': 'true'}])
 	 *
-	 * @param mixed		$arguments
-	 * @return string
+	 * @param mixed		$arguments			Attributes to implode
+	 * @return string						The attributes string
 	 */
 	public function implode_attributes(...$arguments)
 	{
@@ -132,8 +132,8 @@ class implode extends \Twig\Extension\AbstractExtension
 	 * 		'hidden-class': S_POST_HIDDEN,
 	 * }])
 	 *
-	 * @param mixed		$arguments
-	 * @return string					The classes string prepended with a space
+	 * @param mixed		$arguments			The classes to implode
+	 * @return string						The classes string prepended with a space
 	 */
 	public function implode_classes(...$arguments)
 	{
diff --git a/phpBB/phpbb/template/twig/extension/macro.php b/phpBB/phpbb/template/twig/extension/macro.php
index f786a15de1..f8fd4f303b 100644
--- a/phpBB/phpbb/template/twig/extension/macro.php
+++ b/phpBB/phpbb/template/twig/extension/macro.php
@@ -23,7 +23,7 @@ class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension
 	/**
 	 * Constructor.
 	 *
-	 * @param environment	$twig		Twig environment object
+	 * @param environment	$twig			Twig environment object
 	 */
 	public function __construct(environment $twig)
 	{
@@ -33,7 +33,7 @@ class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension
 	/**
 	 * Returns the name of this extension.
 	 *
-	 * @return string The extension name
+	 * @return string						The extension name
 	 */
 	public function getName()
 	{
@@ -43,22 +43,13 @@ class macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension
 	/**
 	 * Returns a list of global variables to add to the existing list.
 	 *
-	 * @return array An array of global variables
+	 * @throws \Twig\Error\Error
+	 * @return array						An array of global variables
 	 */
 	public function getGlobals()
 	{
-		$macros = null;
-
-		try
-		{
-			$macros = $this->twig->loadTemplate('macros.html');
-		}
-		catch (\Twig\Error\Error $e)
-		{
-		}
-
 		return [
-			'macros' => $macros,
+			'macros' => $this->twig->loadTemplate('macros/macros.twig'),
 		];
 	}
 }
diff --git a/phpBB/styles/all/template/macros.html b/phpBB/styles/all/template/macros.html
deleted file mode 100644
index 637140c6dc..0000000000
--- a/phpBB/styles/all/template/macros.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{# Wrapper function #}
-{% macro Icon(type, icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	{% set type = type|capitalize %}
-Hello
-{% endmacro Icon %}
diff --git a/phpBB/styles/all/template/macros/macros.twig b/phpBB/styles/all/template/macros/macros.twig
new file mode 100644
index 0000000000..649d3ee889
--- /dev/null
+++ b/phpBB/styles/all/template/macros/macros.twig
@@ -0,0 +1,34 @@
+{# 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 = {}) %}
+	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ Implode_attributes(attributes) }}></i>
+	{% if title %}<span{% if hidden %} class="sr-only"{% endif %}>{{ lang(title) }}</span>{% endif %}
+{% endmacro Fa %}
+
+{# PNG icons #}
+{% macro Png(icon, classes = '', title = '', hidden = false, attributes = {}) %}
+	<img class="o-icon png png-{{ icon ~ Implode_classes(classes) }}" src="{{ icon|png_path }}" alt="{{ lang(title) }}"{{ _self.attributes(attributes) }}
+{% endmacro Png %}
+
+{# SVG icons #}
+{% macro Svg(icon, classes = '', title = '', hidden = false, attributes = {}) %}
+	{% set title_id = title ? title|lower|replace({' ': '_'}) ~ '-' ~ random() %}
+
+	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ Implode_attributes(attributes) }}>
+		{% if title %}
+			<title id="{{ title_id }}">{{ lang(title) }}</title>
+		{% endif %}
+
+		{{ Svg_clean(icon) }}
+	</svg>
+{% endmacro Svg %}
+
+{% from _self import Icon as Icon %}

From ba5e73bfea1ae6fab1c2eafc9210cc15eec35cea Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Fri, 4 Oct 2019 12:58:39 +0200
Subject: [PATCH 10/25] [ticket/15538] Revert back to Icon function

PHPBB3-15538
---
 .../default/container/services_twig.yml       |  12 --
 phpBB/phpbb/template/twig/extension/icon.php  | 150 ---------------
 .../phpbb/template/twig/extension/implode.php | 181 ------------------
 phpBB/phpbb/template/twig/extension/macro.php |  55 ------
 phpBB/styles/all/template/macros/macros.twig  |  34 ----
 .../styles/prosilver/template/icons/font.html |   4 +
 .../styles/prosilver/template/icons/png.html  |   3 +
 .../styles/prosilver/template/icons/svg.html  |   9 +
 .../styles/prosilver/template/index_body.html |   4 +-
 9 files changed, 17 insertions(+), 435 deletions(-)
 delete mode 100644 phpBB/phpbb/template/twig/extension/icon.php
 delete mode 100644 phpBB/phpbb/template/twig/extension/implode.php
 delete mode 100644 phpBB/phpbb/template/twig/extension/macro.php
 delete mode 100644 phpBB/styles/all/template/macros/macros.twig
 create mode 100644 phpBB/styles/prosilver/template/icons/font.html
 create mode 100644 phpBB/styles/prosilver/template/icons/png.html
 create mode 100644 phpBB/styles/prosilver/template/icons/svg.html

diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml
index 9d9436f89e..3e3d1a4674 100644
--- a/phpBB/config/default/container/services_twig.yml
+++ b/phpBB/config/default/container/services_twig.yml
@@ -61,18 +61,6 @@ services:
         tags:
             - { name: twig.extension }
 
-    template.twig.extensions.implode:
-        class: phpbb\template\twig\extension\implode
-        tags:
-            - { name: twig.extension }
-
-    template.twig.extensions.macro:
-        class: phpbb\template\twig\extension\macro
-        arguments:
-            - '@template.twig.environment'
-        tags:
-            - { name: twig.extension }
-
     template.twig.extensions.routing:
         class: phpbb\template\twig\extension\routing
         arguments:
diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
deleted file mode 100644
index 6c077385f3..0000000000
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?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 filters to add to the existing list.
-	 *
-	 * @return \Twig\TwigFilter[]			Array of twig filters
-	 */
-	public function getFilters()
-	{
-		return [
-			new \Twig\TwigFilter('png_path', [$this, 'png_path'], ['needs_environment' => true]),
-		];
-	}
-
-	/**
-	 * 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('Svg_clean', [$this, 'svg_clean'], ['needs_environment' => true]),
-		];
-	}
-
-	/**
-	 * Create a path to a PNG template icon.
-	 *
-	 * @param environment	$environment	Twig environment object
-	 * @param string		$icon			The icon name
-	 * @return string
-	 */
-	public 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}/theme/icons/png/{$icon}.png";
-	}
-
-	/**
-	 * Load and clean an SVG template icon.
-	 *
-	 * @param environment	$environment	Twig environment object
-	 * @param string		$icon			The icon name
-	 * @return string
-	 */
-	public function svg_clean(environment $environment, $icon)
-	{
-		try
-		{
-			$file = $environment->load('icons/svg/' . $icon . '.svg');
-		}
-		catch (\Twig\Error\Error $e)
-		{
-			return '';
-		}
-
-		$src = $file->getSourceContext();
-		$svg = $src->getCode();
-
-		$doc = new \DOMDocument();
-		$doc->preserveWhiteSpace = false;
-		$doc->strictErrorChecking = false;
-
-		if (!$doc->loadXML($svg))
-		{
-			return '';
-		}
-
-		foreach ($doc->childNodes as $child)
-		{
-			if ($child->nodeType === XML_DOCUMENT_TYPE_NODE)
-			{
-				$child->parentNode->removeChild($child);
-			}
-		}
-
-		$xpath = new \DOMXPath($doc);
-
-		foreach ($xpath->query('//svg | //title') as $element)
-		{
-			if ($element->nodeName === 'svg')
-			{
-				$children = [];
-
-				/** @var \DOMNode $node */
-				foreach ($element->childNodes as $node)
-				{
-					$children[] = $node;
-				}
-
-				/** @var \DOMNode $child */
-				foreach ($children as $child)
-				{
-					$element->parentNode->insertBefore($child, $element);
-				}
-			}
-
-			$element->parentNode->removeChild($element);
-		}
-
-		$string = $doc->saveXML($doc->documentElement, LIBXML_NOEMPTYTAG);
-		$string = preg_replace('/\s+/', ' ', $string);
-
-		return $string;
-	}
-}
diff --git a/phpBB/phpbb/template/twig/extension/implode.php b/phpBB/phpbb/template/twig/extension/implode.php
deleted file mode 100644
index 93c6fe48ae..0000000000
--- a/phpBB/phpbb/template/twig/extension/implode.php
+++ /dev/null
@@ -1,181 +0,0 @@
-<?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;
-
-class implode extends \Twig\Extension\AbstractExtension
-{
-	/**
-	 * Returns the name of this extension.
-	 *
-	 * @return string						The extension name
-	 */
-	public function getName()
-	{
-		return 'implode';
-	}
-
-	/**
-	 * 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('Implode_attributes', [$this, 'implode_attributes']),
-			new \Twig\TwigFunction('Implode_classes', [$this, 'implode_classes']),
-		];
-	}
-
-	/**
-	 * Implode an array of attributes to a string.
-	 *
-	 * This string will be prepended by a space for ease-of-use.
-	 *
-	 * Examples would be:
-	 * Implode_attributes('checked', {'data-ajax': 'true'})
-	 * Implode_attributes(['checked', {'data-ajax': 'true'}])
-	 *
-	 * @param mixed		$arguments			Attributes to implode
-	 * @return string						The attributes string
-	 */
-	public 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 if (is_array($value))
-					{
-						foreach ($value as $k => $v)
-						{
-							if (is_integer($k) && is_string($v))
-							{
-								$attributes[] = $v;
-							}
-							else
-							{
-
-								$attributes[$k] = $v;
-							}
-						}
-					}
-					else
-					{
-						$attributes[$key] = $value;
-					}
-				}
-			}
-		}
-
-		foreach ($attributes as $attribute => $value)
-		{
-			if (is_string($attribute))
-			{
-				$value = is_bool($value) ? ($value ? 'true' : 'false') : $value;
-
-				$string .= ' ' . $attribute . '="' . $value . '"';
-			}
-			else
-			{
-				$string .= ' ' . $value;
-			}
-		}
-
-		return $string;
-	}
-
-	/**
-	 * Implode an array or classes to a string.
-	 *
-	 * This string will be prepended with a space for ease-of-use.
-	 *
-	 * Conditions can be added to the classes, which will determine if the classes is added to the string.
-	 * @see https://twig.symfony.com/doc/2.x/functions/html_classes.html
-	 *
-	 * An example would be:
-	 * Implode_classes('a-class', 'another-class', {
-	 * 		'reported-class': S_POST_REPORTED,
-	 * 		'hidden-class': S_POST_HIDDEN,
-	 * })
-	 *
-	 * This function differs from the html_classes function linked above,
-	 * in that it allows another depth level, so it also supports a single argument.
-	 *
-	 * An example would be:
-	 * Implode_classes(['a-class', 'another-class', {
-	 * 		'reported-class': S_POST_REPORTED,
-	 * 		'hidden-class': S_POST_HIDDEN,
-	 * }])
-	 *
-	 * @param mixed		$arguments			The classes to implode
-	 * @return string						The classes string prepended with a space
-	 */
-	public 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/phpbb/template/twig/extension/macro.php b/phpBB/phpbb/template/twig/extension/macro.php
deleted file mode 100644
index f8fd4f303b..0000000000
--- a/phpBB/phpbb/template/twig/extension/macro.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?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 macro extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface
-{
-	/** @var environment */
-	protected $twig;
-
-	/**
-	 * Constructor.
-	 *
-	 * @param environment	$twig			Twig environment object
-	 */
-	public function __construct(environment $twig)
-	{
-		$this->twig = $twig;
-	}
-
-	/**
-	 * Returns the name of this extension.
-	 *
-	 * @return string						The extension name
-	 */
-	public function getName()
-	{
-		return 'macros';
-	}
-
-	/**
-	 * Returns a list of global variables to add to the existing list.
-	 *
-	 * @throws \Twig\Error\Error
-	 * @return array						An array of global variables
-	 */
-	public function getGlobals()
-	{
-		return [
-			'macros' => $this->twig->loadTemplate('macros/macros.twig'),
-		];
-	}
-}
diff --git a/phpBB/styles/all/template/macros/macros.twig b/phpBB/styles/all/template/macros/macros.twig
deleted file mode 100644
index 649d3ee889..0000000000
--- a/phpBB/styles/all/template/macros/macros.twig
+++ /dev/null
@@ -1,34 +0,0 @@
-{# 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 = {}) %}
-	<i class="o-icon font fa-{{ icon ~ Implode_classes(classes) }}"{% if hidden %}{% if title %} title="{{ lang(title) }}"{% endif %} aria-hidden="true"{% endif %}{{ Implode_attributes(attributes) }}></i>
-	{% if title %}<span{% if hidden %} class="sr-only"{% endif %}>{{ lang(title) }}</span>{% endif %}
-{% endmacro Fa %}
-
-{# PNG icons #}
-{% macro Png(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	<img class="o-icon png png-{{ icon ~ Implode_classes(classes) }}" src="{{ icon|png_path }}" alt="{{ lang(title) }}"{{ _self.attributes(attributes) }}
-{% endmacro Png %}
-
-{# SVG icons #}
-{% macro Svg(icon, classes = '', title = '', hidden = false, attributes = {}) %}
-	{% set title_id = title ? title|lower|replace({' ': '_'}) ~ '-' ~ random() %}
-
-	<svg class="o-icon svg svg-{{ icon ~ Implode_classes(classes) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if title %}{% if hidden %} aria-hidden="true"{% endif %} aria-labelledby="{{ title_id }}"{% endif %} role="img"{{ Implode_attributes(attributes) }}>
-		{% if title %}
-			<title id="{{ title_id }}">{{ lang(title) }}</title>
-		{% endif %}
-
-		{{ Svg_clean(icon) }}
-	</svg>
-{% endmacro Svg %}
-
-{% from _self import Icon as Icon %}
diff --git a/phpBB/styles/prosilver/template/icons/font.html b/phpBB/styles/prosilver/template/icons/font.html
new file mode 100644
index 0000000000..db43dd0d39
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/font.html
@@ -0,0 +1,4 @@
+{% spaceless %}
+	<i class="o-icon font fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %}{{ ATTRIBUTES }}></i>
+	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/png.html b/phpBB/styles/prosilver/template/icons/png.html
new file mode 100644
index 0000000000..48f8b81b88
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/png.html
@@ -0,0 +1,3 @@
+{% spaceless %}
+	<img class="o-icon png png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" src="{{ SOURCE }}" alt="{{ lang(TITLE) }}"{{ ATTRIBUTES }} />
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/icons/svg.html b/phpBB/styles/prosilver/template/icons/svg.html
new file mode 100644
index 0000000000..24a4459058
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/svg.html
@@ -0,0 +1,9 @@
+{% spaceless %}
+	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '_'}) ~ '-' ~ random() %}
+
+	<svg class="o-icon svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if TITLE %}{% if S_HIDDEN %} aria-hidden="true"{% endif %} aria-labelledby="{{ TITLE_ID }}"{% endif %} role="img"{{ ATTRIBUTES }}>
+		{% if TITLE %}<title id="{{ TITLE_ID }}">{{ lang(TITLE) }}</title>{% endif %}
+
+		{{ SOURCE }}
+	</svg>
+{% endspaceless %}
diff --git a/phpBB/styles/prosilver/template/index_body.html b/phpBB/styles/prosilver/template/index_body.html
index 4472786992..94d069b597 100644
--- a/phpBB/styles/prosilver/template/index_body.html
+++ b/phpBB/styles/prosilver/template/index_body.html
@@ -11,8 +11,6 @@
 <!-- ENDIF -->
 <!-- EVENT index_body_markforums_after -->
 
-Test: {{ macros.Icon('fa', 'phone') }}
-
 <!-- INCLUDE forumlist_body.html -->
 
 <!-- EVENT index_body_forumlist_body_after -->
@@ -43,7 +41,7 @@ Test: {{ macros.Icon('fa', 'phone') }}
 		<!-- IF U_VIEWONLINE --><h3><a href="{U_VIEWONLINE}">{L_WHO_IS_ONLINE}</a></h3><!-- ELSE --><h3>{L_WHO_IS_ONLINE}</h3><!-- ENDIF -->
 		<p>
 			<!-- EVENT index_body_block_online_prepend -->
-			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br />
+			{TOTAL_USERS_ONLINE} ({L_ONLINE_EXPLAIN})<br />{RECORD_USERS}<br /> 
 			<!-- IF U_VIEWONLINE -->
 				<br />{LOGGED_IN_USER_LIST}
 				<!-- IF LEGEND --><br /><em>{L_LEGEND}{L_COLON} {LEGEND}</em><!-- ENDIF -->

From a7ae904195fcd25aedd1c43d37798d36f65aa52f Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Fri, 4 Oct 2019 13:44:58 +0200
Subject: [PATCH 11/25] [ticket/15538] Include Icon function

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php | 183 +++++++++++++++++++
 1 file changed, 183 insertions(+)
 create mode 100644 phpBB/phpbb/template/twig/extension/icon.php

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
new file mode 100644
index 0000000000..4936d24e8e
--- /dev/null
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -0,0 +1,183 @@
+<?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|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 = utf8_strtolower($type);
+
+		switch ($type)
+		{
+			case 'font':
+				$source = '';
+			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();
+				$style_path	= $this->user->style['style_path'];
+
+				$source = "{$web_path}styles/{$style_path}/theme/icons/png/{$icon}.png";
+			break;
+
+			case 'svg':
+				try
+				{
+					$file	= $environment->load('icons/svg/' . $icon . '.svg');
+					$source	= $this->prepare_svg($file);
+				}
+				catch (\Twig\Error\Error $e)
+				{
+					return '';
+				}
+			break;
+
+			default:
+				return '';
+			break;
+		}
+
+		try
+		{
+			return $environment->render("icons/{$type}.html", [
+				'ATTRIBUTES'	=> (string) $this->implode_attributes($attributes),
+				'CLASSES'		=> (string) $classes,
+				'ICON'			=> (string) $icon,
+				'SOURCE'		=> (string) $source,
+				'TITLE'			=> (string) $title,
+				'S_HIDDEN'		=> (bool) $hidden,
+			]);
+		}
+		catch (\Twig\Error\Error $e)
+		{
+			return '';
+		}
+	}
+
+	/**
+	 * Prepare an SVG for usage in the template icon.
+	 *
+	 * @param \Twig\TemplateWrapper	$file	The SVG file loaded from the environment
+	 * @return string
+	 */
+	protected function prepare_svg(\Twig\TemplateWrapper $file)
+	{
+		$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($file->render(), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+
+		$xpath = new \DOMXPath($doc);
+
+		// Remove all <svg> and <title> elements
+		foreach ($xpath->query('//svg | //title') as $element)
+		{
+			if ($element->nodeName === 'svg')
+			{
+				$children = [];
+
+				foreach ($element->childNodes as $node)
+				{
+					$children[] = $node;
+				}
+
+				foreach ($children as $child)
+				{
+					$element->parentNode->insertBefore($child, $element);
+				}
+			}
+
+			$element->parentNode->removeChild($element);
+		}
+
+		$string = $doc->saveHTML();
+		$string = preg_replace('/\s+/', ' ', $string);
+
+		return $string;
+	}
+
+	/**
+	 * 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;
+	}
+}

From b4b6ba0a372d49a6fb3d7f5dff4dc0852ffdda91 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Tue, 29 Oct 2019 12:07:53 +0100
Subject: [PATCH 12/25] [ticket/15538] Extract service and enhance icon
 function

PHPBB3-15538
---
 phpBB/config/default/container/services.yml         |  1 +
 phpBB/config/default/container/services_twig.yml    |  7 -------
 .../default/container/services_twig_extensions.yml  |  9 +++++++++
 phpBB/phpbb/template/twig/extension/icon.php        | 13 +++----------
 4 files changed, 13 insertions(+), 17 deletions(-)
 create mode 100644 phpBB/config/default/container/services_twig_extensions.yml

diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml
index 0e9a037290..a7ab26f163 100644
--- a/phpBB/config/default/container/services.yml
+++ b/phpBB/config/default/container/services.yml
@@ -29,6 +29,7 @@ imports:
     - { resource: services_text_formatter.yml }
     - { resource: services_text_reparser.yml }
     - { resource: services_twig.yml }
+    - { resource: services_twig_extensions.yml }
     - { resource: services_ucp.yml }
     - { resource: services_user.yml }
 
diff --git a/phpBB/config/default/container/services_twig.yml b/phpBB/config/default/container/services_twig.yml
index 3e3d1a4674..24ee24484b 100644
--- a/phpBB/config/default/container/services_twig.yml
+++ b/phpBB/config/default/container/services_twig.yml
@@ -54,13 +54,6 @@ services:
         tags:
             - { name: twig.extension }
 
-    template.twig.extensions.icon:
-        class: phpbb\template\twig\extension\icon
-        arguments:
-            - '@user'
-        tags:
-            - { name: twig.extension }
-
     template.twig.extensions.routing:
         class: phpbb\template\twig\extension\routing
         arguments:
diff --git a/phpBB/config/default/container/services_twig_extensions.yml b/phpBB/config/default/container/services_twig_extensions.yml
new file mode 100644
index 0000000000..115d3f1417
--- /dev/null
+++ b/phpBB/config/default/container/services_twig_extensions.yml
@@ -0,0 +1,9 @@
+# Twig extensions not needed by the installer
+
+services:
+    template.twig.extensions.icon:
+        class: phpbb\template\twig\extension\icon
+        arguments:
+            - '@user'
+        tags:
+            - { name: twig.extension }
diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 4936d24e8e..27807c9ecc 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -67,7 +67,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	 */
 	public function icon(environment $environment, $type, $icon, $title = '', $hidden = false, $classes = '', array $attributes = [])
 	{
-		$type = utf8_strtolower($type);
+		$type = strtolower($type);
 
 		switch ($type)
 		{
@@ -141,16 +141,9 @@ class icon extends \Twig\Extension\AbstractExtension
 		{
 			if ($element->nodeName === 'svg')
 			{
-				$children = [];
-
-				foreach ($element->childNodes as $node)
+				while (isset($element->firstChild))
 				{
-					$children[] = $node;
-				}
-
-				foreach ($children as $child)
-				{
-					$element->parentNode->insertBefore($child, $element);
+					$element->parentNode->insertBefore($element->firstChild, $element);
 				}
 			}
 

From fea97cd1153ae5a97c86e2d65e7a538c0f955311 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Thu, 31 Oct 2019 19:36:19 +0100
Subject: [PATCH 13/25] [ticket/15538] Add icon tests

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php  |  14 +-
 tests/template/extension_test.php             | 176 +++++++++++++++++-
 .../templates/extension_icon_test.html        |   1 +
 tests/template/templates/icons/svg/dirty.svg  |   6 +
 tests/template/templates/icons/svg/pencil.svg |   1 +
 tests/template/templates/icons/svg/phone.svg  |   1 +
 6 files changed, 192 insertions(+), 7 deletions(-)
 create mode 100644 tests/template/templates/extension_icon_test.html
 create mode 100644 tests/template/templates/icons/svg/dirty.svg
 create mode 100644 tests/template/templates/icons/svg/pencil.svg
 create mode 100644 tests/template/templates/icons/svg/phone.svg

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 27807c9ecc..b2a98fad3f 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -125,6 +125,9 @@ class icon extends \Twig\Extension\AbstractExtension
 	 */
 	protected function prepare_svg(\Twig\TemplateWrapper $file)
 	{
+		$code = $file->render();
+		$code = preg_replace( "/<\?xml.+?\?>/", '', $code);
+
 		$doc = new \DOMDocument();
 		$doc->preserveWhiteSpace = false;
 
@@ -132,7 +135,16 @@ class icon extends \Twig\Extension\AbstractExtension
 		 * Suppression is needed as DOMDocument does not like HTML5 and SVGs.
 		 * Options parameter prevents $dom->saveHTML() from adding an <html> element.
 		 */
-		@$doc->loadHTML($file->render(), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+		@$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);
 
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 0e9f2110ed..4e8a71b7a9 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -15,7 +15,7 @@ require_once dirname(__FILE__) . '/template_test_case.php';
 
 class phpbb_template_extension_test extends phpbb_template_template_test_case
 {
-	protected function setup_engine(array $new_config = array())
+	protected function setup_engine(array $new_config = [])
 	{
 		global $config, $phpbb_container, $phpbb_dispatcher, $phpbb_root_path, $phpEx;
 
@@ -28,6 +28,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 		$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
 		$this->lang = $lang = new \phpbb\language\language($lang_loader);
 		$this->user = new \phpbb\user($lang, '\phpbb\datetime');
+		$this->user->style['style_path'] = 'prosilver';
 
 		global $auth, $request, $symfony_request, $user;
 		$user = new phpbb_mock_user();
@@ -73,7 +74,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 
 		$cache_path = $phpbb_root_path . 'cache/twig';
 		$context = new \phpbb\template\context();
-		$loader = new \phpbb\template\twig\loader('');
+		$loader = new \phpbb\template\twig\loader([]);
 		$twig = new \phpbb\template\twig\environment(
 			$config,
 			$filesystem,
@@ -82,12 +83,12 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 			null,
 			$loader,
 			new \phpbb\event\dispatcher($phpbb_container),
-			array(
+			[
 				'cache'			=> false,
 				'debug'			=> false,
 				'auto_reload'	=> true,
 				'autoescape'	=> false,
-			)
+			]
 		);
 		$this->template = new phpbb\template\twig\twig(
 			$phpbb_path_helper,
@@ -100,11 +101,16 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				new \phpbb\template\twig\extension($context, $twig, $this->lang),
 				new \phpbb\template\twig\extension\avatar(),
 				new \phpbb\template\twig\extension\config($config),
+				new \phpbb\template\twig\extension\icon($this->user),
 				new \phpbb\template\twig\extension\username(),
 			]
 		);
 		$twig->setLexer(new \phpbb\template\twig\lexer($twig));
-		$this->template->set_custom_style('tests', $this->template_path);
+		$this->template->set_style();
+		$this->template->set_custom_style('tests', [
+			$this->template_path,
+			$phpbb_root_path . 'styles/prosilver/template',
+		]);
 	}
 
 	public function data_template_extensions()
@@ -253,8 +259,166 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 	/**
 	 * @dataProvider data_template_extensions
 	 */
-	public function test_get_user_avatar($file, $vars, $block_vars, $destroy_array, $expected, $lang_vars = [])
+	public function test_template_extensions($file, $vars, $block_vars, $destroy_array, $expected, $lang_vars = [])
 	{
 		$this->run_template($file, $vars, $block_vars, $destroy_array, $expected, $lang_vars);
 	}
+
+	public function data_template_icon_extension()
+	{
+		return [
+			/** Font: default */
+			[
+				[
+					'type'			=> 'font',
+					'icon'			=> 'phone',
+					'title'			=> 'ICON_PHONE',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_PHONE'	=> 'Phone icon',
+				],
+				'<i class="o-icon font fa-phone"></i><span>Phone icon</span>',
+
+			],
+			/** Font: all options */
+			[
+				[
+					'type'			=> 'font',
+					'icon'			=> 'pencil',
+					'title'			=> 'ICON_PENCIL',
+					'hidden'		=> true,
+					'classes'		=> 'a-class another-class',
+					'attributes'	=> [
+						'data-attr-1'	=> 'true',
+						'data-attr-2'	=> 'two',
+					],
+				],
+				[
+					'ICON_PENCIL'	=> 'Pencil icon',
+				],
+				'<i class="o-icon font fa-pencil a-class another-class" title="Pencil icon" aria-hidden="true" data-attr-1="true" data-attr-2="two"></i>
+				<span class="sr-only">Pencil icon</span>'
+			],
+			/** PNG: default */
+			[
+				[
+					'type'			=> 'png',
+					'icon'			=> 'phone',
+					'title'			=> 'ICON_PHONE',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_PHONE'	=> 'Phone icon',
+				],
+				'<img class="o-icon png png-phone" src="phpBB/styles/prosilver/theme/icons/png/phone.png" alt="Phone icon" />',
+			],
+			/** PNG: all options */
+			[
+				[
+					'type'			=> 'png',
+					'icon'			=> 'pencil',
+					'title'			=> 'ICON_PENCIL',
+					'hidden'		=> true,
+					'classes'		=> 'my-class',
+					'attributes'	=> [
+						'data-url'		=> 'my-test-url/test-page.php?u=2',
+					],
+				],
+				[
+					'ICON_PENCIL'	=> 'Pencil icon',
+				],
+				'<img class="o-icon png png-pencil my-class" src="phpBB/styles/prosilver/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
+			],
+			/** SVG: default */
+			[
+				[
+					'type'			=> 'svg',
+					'icon'			=> 'phone',
+					'title'			=> 'ICON_PHONE',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_PHONE'	=> 'Phone icon',
+				],
+				'<svg class="o-icon svg svg-phone" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-labelledby="icon_phone-123456789" role="img">
+					<title id="icon_phone-123456789">Phone icon</title>
+					<path fill="none" d="M0 0h24v24H0z"></path>
+					<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"></path>
+				</svg>',
+			],
+			/** SVG: all options */
+			[
+				[
+					'type'			=> 'svg',
+					'icon'			=> 'pencil',
+					'title'			=> 'ICON_PENCIL',
+					'hidden'		=> true,
+					'classes'		=> 'my-svg-class',
+					'attributes'	=> [
+						'data-ajax'		=> 'my_ajax_callback',
+					],
+				],
+				[
+					'ICON_PENCIL'	=> 'Pencil icon',
+				],
+				'<svg class="o-icon svg svg-pencil my-svg-class" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" aria-labelledby="icon_pencil-123456789" role="img" data-ajax="my_ajax_callback">
+					<title id="icon_pencil-123456789">Pencil icon</title>
+					<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path>
+					<path d="M0 0h24v24H0z" fill="none"></path>
+				</svg>',
+			],
+			/** SVG: Sanitization */
+			[
+				[
+					'type'			=> 'svg',
+					'icon'			=> 'dirty',
+					'title'			=> '',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[],
+				'<svg class="o-icon svg svg-dirty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img">
+					<path fill-rule="evenodd" d="M139.05,117.4938 C139.05,129.1958 137.603,139.9498 134.718,149.7578 C131.832,159.5708 127.634,168.0768 122.129,175.2728 C116.623,182.4748 109.764,188.0558 101.554,192.0128 C93.344,195.9698 83.915,197.9538 73.267,197.9538 C65.142,197.9538 57.877,195.9278 51.473,191.8788 C45.064,187.8288 40.057,182.6558 36.45,176.3528 L36.45,240.6138 L25.194,240.6138 C21.976,240.6138 18.85,240.0268 15.811,238.8578 C12.774,237.6858 10.091,236.0228 7.77,233.8638 C5.446,231.7038 3.569,229.0918 2.144,226.0338 C0.713,222.9698 0,219.4608 0,215.5038 L0,127.4828 C0,115.4258 1.393,104.3558 4.185,94.2728 C6.974,84.1948 11.34,75.5548 17.28,68.3538 C23.22,61.1558 30.78,55.5748 39.96,51.6128 C49.14,47.6558 60.117,45.6728 72.9,45.6728 C82.62,45.6728 91.53,47.6978 99.63,51.7488 C107.729,55.7978 114.703,61.1558 120.555,67.8138 C126.402,74.4748 130.95,82.1238 134.19,90.7628 C137.43,99.4038 139.05,108.3128 139.05,117.4938 Z M101.79,126.9438 C101.79,109.6638 98.942,97.1548 93.247,89.4138 C87.552,81.6758 78.831,77.8028 67.087,77.8028 C56.966,77.8028 49.241,81.7178 43.909,89.5478 C38.576,97.3788 35.91,107.5958 35.91,120.1938 C35.91,134.7728 39.205,146.0248 45.803,153.9438 C52.401,161.8658 61.121,165.8238 71.968,165.8238 C80.823,165.8238 88.007,162.2708 93.521,155.1588 C99.031,148.0488 101.79,138.6458 101.79,126.9438 Z M267.5684,194.7134 C260.0084,194.7134 254.0224,192.6464 249.6134,188.5034 C245.2014,184.3644 242.9994,178.3364 242.9994,170.4134 L242.9994,111.0134 C242.9994,105.0734 242.2264,99.9894 240.6914,95.7584 C239.1554,91.5304 237.0754,88.1094 234.4514,85.4984 C231.8274,82.8914 228.7524,80.9544 225.2244,79.6934 C221.6984,78.4364 217.9434,77.8034 213.9644,77.8034 C211.0714,77.8034 208.0844,78.3894 205.0124,79.5584 C201.9374,80.7314 199.0894,82.6634 196.4654,85.3634 C193.8414,88.0634 191.7194,91.5734 190.0904,95.8934 C188.4634,100.2134 187.6484,105.6134 187.6484,112.0934 L187.6484,194.7134 L175.8574,194.7134 C167.2764,194.7134 161.0244,192.5534 157.0914,188.2334 C153.1604,183.9134 151.1984,177.9734 151.1984,170.4134 L151.1984,0.3134 L162.4544,0.3134 C171.0314,0.3134 177.4184,2.4734 181.6204,6.7934 C185.8174,11.1134 187.9194,16.6944 187.9194,23.5334 L188.1884,65.1134 C189.4454,62.9534 191.2014,60.7514 193.4534,58.4984 C195.7024,56.2504 198.1784,54.1784 200.8794,52.2884 C203.5784,50.3984 206.5484,48.8244 209.7894,47.5634 C213.0284,46.3064 216.3574,45.6734 219.7784,45.6734 C238.6784,45.6734 253.3474,51.1194 263.7894,62.0084 C274.2254,72.9014 279.4484,88.6954 279.4484,109.3934 L279.4484,194.7134 L267.5684,194.7134 Z M436.0442,117.4938 C436.0442,129.1958 434.5982,139.9498 431.7122,149.7578 C428.8272,159.5708 424.6282,168.0768 419.1242,175.2728 C413.6182,182.4748 406.7582,188.0558 398.5482,192.0128 C390.3392,195.9698 380.9102,197.9538 370.2622,197.9538 C362.1372,197.9538 354.8722,195.9278 348.4682,191.8788 C342.0592,187.8288 337.0522,182.6558 333.4442,176.3528 L333.4442,240.6138 L322.1882,240.6138 C318.9702,240.6138 315.8442,240.0268 312.8062,238.8578 C309.7682,237.6858 307.0862,236.0228 304.7652,233.8638 C302.4412,231.7038 300.5632,229.0918 299.1382,226.0338 C297.7082,222.9698 296.9942,219.4608 296.9942,215.5038 L296.9942,127.4828 C296.9942,115.4258 298.3872,104.3558 301.1802,94.2728 C303.9682,84.1948 308.3352,75.5548 314.2742,68.3538 C320.2152,61.1558 327.7742,55.5748 336.9542,51.6128 C346.1352,47.6558 357.1112,45.6728 369.8942,45.6728 C379.6142,45.6728 388.5242,47.6978 396.6252,51.7488 C404.7242,55.7978 411.6982,61.1558 417.5502,67.8138 C423.3972,74.4748 427.9442,82.1238 431.1842,90.7628 C434.4252,99.4038 436.0442,108.3128 436.0442,117.4938 Z M398.7842,126.9438 C398.7842,109.6638 395.9362,97.1548 390.2412,89.4138 C384.5462,81.6758 375.8262,77.8028 364.0812,77.8028 C353.9602,77.8028 346.2352,81.7178 340.9032,89.5478 C335.5712,97.3788 332.9042,107.5958 332.9042,120.1938 C332.9042,134.7728 336.1992,146.0248 342.7982,153.9438 C349.3952,161.8658 358.1162,165.8238 368.9622,165.8238 C377.8172,165.8238 385.0022,162.2708 390.5152,155.1588 C396.0252,148.0488 398.7842,138.6458 398.7842,126.9438 Z M581.5745,137.4732 C581.5745,146.8342 579.8615,155.1152 576.4445,162.3132 C573.0225,169.5152 568.3855,175.5892 562.5395,180.5382 C556.6875,185.4912 549.9375,189.2252 542.2895,191.7432 C534.6355,194.2662 526.5835,195.5232 518.1245,195.5232 L498.1435,195.5232 C463.7605,195.5232 446.5745,180.8552 446.5745,151.5132 L446.5745,17.3232 C448.7345,16.6062 451.7925,15.7502 455.7535,14.7582 C459.7105,13.7712 463.9895,12.8262 468.5795,11.9232 C473.1685,11.0252 477.8475,10.3032 482.6195,9.7632 C487.3855,9.2232 491.6645,8.9532 495.4445,8.9532 L517.8535,8.9532 C526.6705,8.9532 534.7715,10.0792 542.1545,12.3282 C549.5325,14.5812 555.9235,17.8212 561.3245,22.0482 C566.7235,26.2802 570.9095,31.4092 573.8785,37.4382 C576.8485,43.4712 578.3345,50.2632 578.3345,57.8232 C578.3345,68.0832 575.7225,76.5002 570.5035,83.0692 C565.2815,89.6412 558.0845,94.2732 548.9045,96.9732 C553.5835,98.2352 557.9025,100.3062 561.8645,103.1832 C565.8215,106.0652 569.2895,109.3512 572.2585,113.0382 C575.2285,116.7302 577.5245,120.7332 579.1435,125.0532 C580.7635,129.3732 581.5745,133.5162 581.5745,137.4732 Z M544.3135,138.5532 C544.3135,128.4752 541.7495,121.5432 536.6195,117.7632 C531.4895,113.9832 523.5245,112.0932 512.7235,112.0932 L483.2935,112.0932 L483.2935,150.1632 C483.2935,153.4042 484.8215,156.1502 487.8835,158.3982 C490.9425,160.6512 494.8115,161.7732 499.4945,161.7732 L514.6145,161.7732 C524.6925,161.7732 532.1645,159.6592 537.0245,155.4282 C541.8835,151.2012 544.3135,145.5742 544.3135,138.5532 Z M541.3435,61.0632 C541.3435,57.1062 540.4875,53.7312 538.7795,50.9382 C537.0665,48.1502 534.8645,45.9432 532.1645,44.3232 C529.4635,42.7032 526.4015,41.5342 522.9845,40.8132 C519.5625,40.0962 516.1415,39.7332 512.7235,39.7332 L498.9545,39.7332 C496.6125,39.7332 494.0005,39.9612 491.1245,40.4082 C488.2425,40.8602 485.5425,41.2642 483.0245,41.6232 L483.0245,83.4732 L511.3745,83.4732 C519.6515,83.4732 526.7175,81.7182 532.5695,78.2082 C538.4165,74.6982 541.3435,68.9862 541.3435,61.0632 Z M727.3733,137.4732 C727.3733,146.8342 725.6603,155.1152 722.2433,162.3132 C718.8213,169.5152 714.1843,175.5892 708.3383,180.5382 C702.4863,185.4912 695.7363,189.2252 688.0883,191.7432 C680.4343,194.2662 672.3823,195.5232 663.9233,195.5232 L643.9423,195.5232 C609.5593,195.5232 592.3733,180.8552 592.3733,151.5132 L592.3733,17.3232 C594.5333,16.6062 597.5913,15.7502 601.5523,14.7582 C605.5093,13.7712 609.7883,12.8262 614.3783,11.9232 C618.9673,11.0252 623.6463,10.3032 628.4183,9.7632 C633.1843,9.2232 637.4633,8.9532 641.2433,8.9532 L663.6523,8.9532 C672.4693,8.9532 680.5703,10.0792 687.9533,12.3282 C695.3313,14.5812 701.7223,17.8212 707.1233,22.0482 C712.5223,26.2802 716.7083,31.4092 719.6773,37.4382 C722.6473,43.4712 724.1333,50.2632 724.1333,57.8232 C724.1333,68.0832 721.5213,76.5002 716.3023,83.0692 C711.0803,89.6412 703.8833,94.2732 694.7033,96.9732 C699.3823,98.2352 703.7013,100.3062 707.6633,103.1832 C711.6203,106.0652 715.0883,109.3512 718.0573,113.0382 C721.0273,116.7302 723.3233,120.7332 724.9423,125.0532 C726.5623,129.3732 727.3733,133.5162 727.3733,137.4732 Z M690.1123,138.5532 C690.1123,128.4752 687.5483,121.5432 682.4183,117.7632 C677.2883,113.9832 669.3233,112.0932 658.5223,112.0932 L629.0923,112.0932 L629.0923,150.1632 C629.0923,153.4042 630.6203,156.1502 633.6823,158.3982 C636.7413,160.6512 640.6103,161.7732 645.2933,161.7732 L660.4133,161.7732 C670.4913,161.7732 677.9633,159.6592 682.8233,155.4282 C687.6823,151.2012 690.1123,145.5742 690.1123,138.5532 Z M687.1423,61.0632 C687.1423,57.1062 686.2863,53.7312 684.5783,50.9382 C682.8653,48.1502 680.6633,45.9432 677.9633,44.3232 C675.2623,42.7032 672.2003,41.5342 668.7833,40.8132 C665.3613,40.0962 661.9403,39.7332 658.5223,39.7332 L644.7533,39.7332 C642.4113,39.7332 639.7993,39.9612 636.9233,40.4082 C634.0413,40.8602 631.3413,41.2642 628.8233,41.6232 L628.8233,83.4732 L657.1733,83.4732 C665.4503,83.4732 672.5163,81.7182 678.3683,78.2082 C684.2153,74.6982 687.1423,68.9862 687.1423,61.0632z"></path>
+				</svg>',
+			],
+		];
+	}
+
+	/**
+	 * @dataProvider data_template_icon_extension
+	 */
+	public function test_template_icon_extension($vars, $lang_vars, $expected)
+	{
+		$file = 'extension_icon_test.html';
+
+		$this->template->set_filenames(array('test' => $file));
+		$this->template->assign_vars($vars);
+
+		foreach ($lang_vars as $name => $value)
+		{
+			self::$language_reflection_lang->setValue($this->lang, array_merge(
+				self::$language_reflection_lang->getValue($this->lang),
+				[$name => $value]
+			));
+		}
+
+		$expected = str_replace(["\n", "\r", "\t"], '', $expected);
+		$output = str_replace(["\n", "\r", "\t"], '', $this->display('test'));
+
+		if ($vars['type'] === 'svg')
+		{
+			$prefix = strtolower($vars['title']) . '-';
+			$output = preg_replace('/' . $prefix . '\d+/', $prefix . '123456789', $output);
+		}
+
+		$this->assertEquals($expected, $output, "Testing {$file}");
+	}
 }
diff --git a/tests/template/templates/extension_icon_test.html b/tests/template/templates/extension_icon_test.html
new file mode 100644
index 0000000000..4ea6eb0410
--- /dev/null
+++ b/tests/template/templates/extension_icon_test.html
@@ -0,0 +1 @@
+{{ Icon(type, icon, title, hidden, classes, attributes) }}
diff --git a/tests/template/templates/icons/svg/dirty.svg b/tests/template/templates/icons/svg/dirty.svg
new file mode 100644
index 0000000000..29c1500ffe
--- /dev/null
+++ b/tests/template/templates/icons/svg/dirty.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg viewBox="0 0 728 242" xmlns="http://www.w3.org/2000/svg" class="c-hero-logo t-hero-logo" aria-labelledby="site_title">
+	<title id="site_title">phpBB</title>
+	<path fill-rule="evenodd" d="M139.05,117.4938 C139.05,129.1958 137.603,139.9498 134.718,149.7578 C131.832,159.5708 127.634,168.0768 122.129,175.2728 C116.623,182.4748 109.764,188.0558 101.554,192.0128 C93.344,195.9698 83.915,197.9538 73.267,197.9538 C65.142,197.9538 57.877,195.9278 51.473,191.8788 C45.064,187.8288 40.057,182.6558 36.45,176.3528 L36.45,240.6138 L25.194,240.6138 C21.976,240.6138 18.85,240.0268 15.811,238.8578 C12.774,237.6858 10.091,236.0228 7.77,233.8638 C5.446,231.7038 3.569,229.0918 2.144,226.0338 C0.713,222.9698 0,219.4608 0,215.5038 L0,127.4828 C0,115.4258 1.393,104.3558 4.185,94.2728 C6.974,84.1948 11.34,75.5548 17.28,68.3538 C23.22,61.1558 30.78,55.5748 39.96,51.6128 C49.14,47.6558 60.117,45.6728 72.9,45.6728 C82.62,45.6728 91.53,47.6978 99.63,51.7488 C107.729,55.7978 114.703,61.1558 120.555,67.8138 C126.402,74.4748 130.95,82.1238 134.19,90.7628 C137.43,99.4038 139.05,108.3128 139.05,117.4938 Z M101.79,126.9438 C101.79,109.6638 98.942,97.1548 93.247,89.4138 C87.552,81.6758 78.831,77.8028 67.087,77.8028 C56.966,77.8028 49.241,81.7178 43.909,89.5478 C38.576,97.3788 35.91,107.5958 35.91,120.1938 C35.91,134.7728 39.205,146.0248 45.803,153.9438 C52.401,161.8658 61.121,165.8238 71.968,165.8238 C80.823,165.8238 88.007,162.2708 93.521,155.1588 C99.031,148.0488 101.79,138.6458 101.79,126.9438 Z M267.5684,194.7134 C260.0084,194.7134 254.0224,192.6464 249.6134,188.5034 C245.2014,184.3644 242.9994,178.3364 242.9994,170.4134 L242.9994,111.0134 C242.9994,105.0734 242.2264,99.9894 240.6914,95.7584 C239.1554,91.5304 237.0754,88.1094 234.4514,85.4984 C231.8274,82.8914 228.7524,80.9544 225.2244,79.6934 C221.6984,78.4364 217.9434,77.8034 213.9644,77.8034 C211.0714,77.8034 208.0844,78.3894 205.0124,79.5584 C201.9374,80.7314 199.0894,82.6634 196.4654,85.3634 C193.8414,88.0634 191.7194,91.5734 190.0904,95.8934 C188.4634,100.2134 187.6484,105.6134 187.6484,112.0934 L187.6484,194.7134 L175.8574,194.7134 C167.2764,194.7134 161.0244,192.5534 157.0914,188.2334 C153.1604,183.9134 151.1984,177.9734 151.1984,170.4134 L151.1984,0.3134 L162.4544,0.3134 C171.0314,0.3134 177.4184,2.4734 181.6204,6.7934 C185.8174,11.1134 187.9194,16.6944 187.9194,23.5334 L188.1884,65.1134 C189.4454,62.9534 191.2014,60.7514 193.4534,58.4984 C195.7024,56.2504 198.1784,54.1784 200.8794,52.2884 C203.5784,50.3984 206.5484,48.8244 209.7894,47.5634 C213.0284,46.3064 216.3574,45.6734 219.7784,45.6734 C238.6784,45.6734 253.3474,51.1194 263.7894,62.0084 C274.2254,72.9014 279.4484,88.6954 279.4484,109.3934 L279.4484,194.7134 L267.5684,194.7134 Z M436.0442,117.4938 C436.0442,129.1958 434.5982,139.9498 431.7122,149.7578 C428.8272,159.5708 424.6282,168.0768 419.1242,175.2728 C413.6182,182.4748 406.7582,188.0558 398.5482,192.0128 C390.3392,195.9698 380.9102,197.9538 370.2622,197.9538 C362.1372,197.9538 354.8722,195.9278 348.4682,191.8788 C342.0592,187.8288 337.0522,182.6558 333.4442,176.3528 L333.4442,240.6138 L322.1882,240.6138 C318.9702,240.6138 315.8442,240.0268 312.8062,238.8578 C309.7682,237.6858 307.0862,236.0228 304.7652,233.8638 C302.4412,231.7038 300.5632,229.0918 299.1382,226.0338 C297.7082,222.9698 296.9942,219.4608 296.9942,215.5038 L296.9942,127.4828 C296.9942,115.4258 298.3872,104.3558 301.1802,94.2728 C303.9682,84.1948 308.3352,75.5548 314.2742,68.3538 C320.2152,61.1558 327.7742,55.5748 336.9542,51.6128 C346.1352,47.6558 357.1112,45.6728 369.8942,45.6728 C379.6142,45.6728 388.5242,47.6978 396.6252,51.7488 C404.7242,55.7978 411.6982,61.1558 417.5502,67.8138 C423.3972,74.4748 427.9442,82.1238 431.1842,90.7628 C434.4252,99.4038 436.0442,108.3128 436.0442,117.4938 Z M398.7842,126.9438 C398.7842,109.6638 395.9362,97.1548 390.2412,89.4138 C384.5462,81.6758 375.8262,77.8028 364.0812,77.8028 C353.9602,77.8028 346.2352,81.7178 340.9032,89.5478 C335.5712,97.3788 332.9042,107.5958 332.9042,120.1938 C332.9042,134.7728 336.1992,146.0248 342.7982,153.9438 C349.3952,161.8658 358.1162,165.8238 368.9622,165.8238 C377.8172,165.8238 385.0022,162.2708 390.5152,155.1588 C396.0252,148.0488 398.7842,138.6458 398.7842,126.9438 Z M581.5745,137.4732 C581.5745,146.8342 579.8615,155.1152 576.4445,162.3132 C573.0225,169.5152 568.3855,175.5892 562.5395,180.5382 C556.6875,185.4912 549.9375,189.2252 542.2895,191.7432 C534.6355,194.2662 526.5835,195.5232 518.1245,195.5232 L498.1435,195.5232 C463.7605,195.5232 446.5745,180.8552 446.5745,151.5132 L446.5745,17.3232 C448.7345,16.6062 451.7925,15.7502 455.7535,14.7582 C459.7105,13.7712 463.9895,12.8262 468.5795,11.9232 C473.1685,11.0252 477.8475,10.3032 482.6195,9.7632 C487.3855,9.2232 491.6645,8.9532 495.4445,8.9532 L517.8535,8.9532 C526.6705,8.9532 534.7715,10.0792 542.1545,12.3282 C549.5325,14.5812 555.9235,17.8212 561.3245,22.0482 C566.7235,26.2802 570.9095,31.4092 573.8785,37.4382 C576.8485,43.4712 578.3345,50.2632 578.3345,57.8232 C578.3345,68.0832 575.7225,76.5002 570.5035,83.0692 C565.2815,89.6412 558.0845,94.2732 548.9045,96.9732 C553.5835,98.2352 557.9025,100.3062 561.8645,103.1832 C565.8215,106.0652 569.2895,109.3512 572.2585,113.0382 C575.2285,116.7302 577.5245,120.7332 579.1435,125.0532 C580.7635,129.3732 581.5745,133.5162 581.5745,137.4732 Z M544.3135,138.5532 C544.3135,128.4752 541.7495,121.5432 536.6195,117.7632 C531.4895,113.9832 523.5245,112.0932 512.7235,112.0932 L483.2935,112.0932 L483.2935,150.1632 C483.2935,153.4042 484.8215,156.1502 487.8835,158.3982 C490.9425,160.6512 494.8115,161.7732 499.4945,161.7732 L514.6145,161.7732 C524.6925,161.7732 532.1645,159.6592 537.0245,155.4282 C541.8835,151.2012 544.3135,145.5742 544.3135,138.5532 Z M541.3435,61.0632 C541.3435,57.1062 540.4875,53.7312 538.7795,50.9382 C537.0665,48.1502 534.8645,45.9432 532.1645,44.3232 C529.4635,42.7032 526.4015,41.5342 522.9845,40.8132 C519.5625,40.0962 516.1415,39.7332 512.7235,39.7332 L498.9545,39.7332 C496.6125,39.7332 494.0005,39.9612 491.1245,40.4082 C488.2425,40.8602 485.5425,41.2642 483.0245,41.6232 L483.0245,83.4732 L511.3745,83.4732 C519.6515,83.4732 526.7175,81.7182 532.5695,78.2082 C538.4165,74.6982 541.3435,68.9862 541.3435,61.0632 Z M727.3733,137.4732 C727.3733,146.8342 725.6603,155.1152 722.2433,162.3132 C718.8213,169.5152 714.1843,175.5892 708.3383,180.5382 C702.4863,185.4912 695.7363,189.2252 688.0883,191.7432 C680.4343,194.2662 672.3823,195.5232 663.9233,195.5232 L643.9423,195.5232 C609.5593,195.5232 592.3733,180.8552 592.3733,151.5132 L592.3733,17.3232 C594.5333,16.6062 597.5913,15.7502 601.5523,14.7582 C605.5093,13.7712 609.7883,12.8262 614.3783,11.9232 C618.9673,11.0252 623.6463,10.3032 628.4183,9.7632 C633.1843,9.2232 637.4633,8.9532 641.2433,8.9532 L663.6523,8.9532 C672.4693,8.9532 680.5703,10.0792 687.9533,12.3282 C695.3313,14.5812 701.7223,17.8212 707.1233,22.0482 C712.5223,26.2802 716.7083,31.4092 719.6773,37.4382 C722.6473,43.4712 724.1333,50.2632 724.1333,57.8232 C724.1333,68.0832 721.5213,76.5002 716.3023,83.0692 C711.0803,89.6412 703.8833,94.2732 694.7033,96.9732 C699.3823,98.2352 703.7013,100.3062 707.6633,103.1832 C711.6203,106.0652 715.0883,109.3512 718.0573,113.0382 C721.0273,116.7302 723.3233,120.7332 724.9423,125.0532 C726.5623,129.3732 727.3733,133.5162 727.3733,137.4732 Z M690.1123,138.5532 C690.1123,128.4752 687.5483,121.5432 682.4183,117.7632 C677.2883,113.9832 669.3233,112.0932 658.5223,112.0932 L629.0923,112.0932 L629.0923,150.1632 C629.0923,153.4042 630.6203,156.1502 633.6823,158.3982 C636.7413,160.6512 640.6103,161.7732 645.2933,161.7732 L660.4133,161.7732 C670.4913,161.7732 677.9633,159.6592 682.8233,155.4282 C687.6823,151.2012 690.1123,145.5742 690.1123,138.5532 Z M687.1423,61.0632 C687.1423,57.1062 686.2863,53.7312 684.5783,50.9382 C682.8653,48.1502 680.6633,45.9432 677.9633,44.3232 C675.2623,42.7032 672.2003,41.5342 668.7833,40.8132 C665.3613,40.0962 661.9403,39.7332 658.5223,39.7332 L644.7533,39.7332 C642.4113,39.7332 639.7993,39.9612 636.9233,40.4082 C634.0413,40.8602 631.3413,41.2642 628.8233,41.6232 L628.8233,83.4732 L657.1733,83.4732 C665.4503,83.4732 672.5163,81.7182 678.3683,78.2082 C684.2153,74.6982 687.1423,68.9862 687.1423,61.0632z"></path>
+</svg>
diff --git a/tests/template/templates/icons/svg/pencil.svg b/tests/template/templates/icons/svg/pencil.svg
new file mode 100644
index 0000000000..c9c021d811
--- /dev/null
+++ b/tests/template/templates/icons/svg/pencil.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>My fake title!</title><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
diff --git a/tests/template/templates/icons/svg/phone.svg b/tests/template/templates/icons/svg/phone.svg
new file mode 100644
index 0000000000..5fbfe196ba
--- /dev/null
+++ b/tests/template/templates/icons/svg/phone.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"/></svg>

From 8e8532561bf2e00cf4b89d71fc03b3f574da4455 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Thu, 31 Oct 2019 19:38:54 +0100
Subject: [PATCH 14/25] [ticket/15538] Amend style name and remove set_style

PHPBB3-15538
---
 tests/template/extension_test.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 4e8a71b7a9..d40a749aa2 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -28,7 +28,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 		$lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx);
 		$this->lang = $lang = new \phpbb\language\language($lang_loader);
 		$this->user = new \phpbb\user($lang, '\phpbb\datetime');
-		$this->user->style['style_path'] = 'prosilver';
+		$this->user->style['style_path'] = 'chameleon';
 
 		global $auth, $request, $symfony_request, $user;
 		$user = new phpbb_mock_user();
@@ -106,7 +106,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 			]
 		);
 		$twig->setLexer(new \phpbb\template\twig\lexer($twig));
-		$this->template->set_style();
+
 		$this->template->set_custom_style('tests', [
 			$this->template_path,
 			$phpbb_root_path . 'styles/prosilver/template',
@@ -315,7 +315,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PHONE'	=> 'Phone icon',
 				],
-				'<img class="o-icon png png-phone" src="phpBB/styles/prosilver/theme/icons/png/phone.png" alt="Phone icon" />',
+				'<img class="o-icon png png-phone" src="phpBB/styles/chameleon/theme/icons/png/phone.png" alt="Phone icon" />',
 			],
 			/** PNG: all options */
 			[
@@ -332,7 +332,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<img class="o-icon png png-pencil my-class" src="phpBB/styles/prosilver/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
+				'<img class="o-icon png png-pencil my-class" src="phpBB/styles/chameleon/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
 			],
 			/** SVG: default */
 			[

From 0eb9912af858797f89286162e82066783a4424e6 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Wed, 6 Nov 2019 14:51:36 +0100
Subject: [PATCH 15/25] [ticket/15538] Add iconify

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php  |  3 +-
 .../prosilver/template/icons/iconify.html     |  4 +++
 tests/template/extension_test.php             | 31 +++++++++++++++++++
 3 files changed, 37 insertions(+), 1 deletion(-)
 create mode 100644 phpBB/styles/prosilver/template/icons/iconify.html

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index b2a98fad3f..e3300eab80 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -56,7 +56,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	 * Generate icon HTML for use in the template, depending on the mode.
 	 *
 	 * @param environment	$environment	Twig environment object
-	 * @param string		$type			Icon type (font|png|svg)
+	 * @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
@@ -72,6 +72,7 @@ class icon extends \Twig\Extension\AbstractExtension
 		switch ($type)
 		{
 			case 'font':
+			case 'iconify':
 				$source = '';
 			break;
 
diff --git a/phpBB/styles/prosilver/template/icons/iconify.html b/phpBB/styles/prosilver/template/icons/iconify.html
new file mode 100644
index 0000000000..74820bafc0
--- /dev/null
+++ b/phpBB/styles/prosilver/template/icons/iconify.html
@@ -0,0 +1,4 @@
+{% spaceless %}
+	<i class="iconify o-icon {{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %}{{ ATTRIBUTES }}></i>
+	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
+{% endspaceless %}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index d40a749aa2..fec06fe296 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -302,6 +302,37 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				'<i class="o-icon font fa-pencil a-class another-class" title="Pencil icon" aria-hidden="true" data-attr-1="true" data-attr-2="two"></i>
 				<span class="sr-only">Pencil icon</span>'
 			],
+			/** Iconify: default */
+			[
+				[
+					'type'			=> 'iconify',
+					'icon'			=> 'phone',
+					'title'			=> '',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[],
+				'<i class="iconify o-icon phone"></i>',
+			],
+			/** Iconify: all options */
+			[
+				[
+					'type'			=> 'iconify',
+					'icon'			=> 'pencil',
+					'title'			=> 'ICON_PENCIL',
+					'hidden'		=> true,
+					'classes'		=> 'icon-lg',
+					'attributes'	=> [
+						'data-swap'		=> 'Swap text',
+					],
+				],
+				[
+					'ICON_PENCIL'	=> 'Pencil icon',
+				],
+				'<i class="iconify o-icon pencil icon-lg" title="Pencil icon" aria-hidden="true" data-swap="Swap text"></i>
+				<span class="sr-only">Pencil icon</span>',
+			],
 			/** PNG: default */
 			[
 				[

From fd95dfe5b39060542b1862c07361e8b5dd5482dd Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Thu, 7 Nov 2019 10:55:51 +0100
Subject: [PATCH 16/25] [ticket/15538] Amend iconfiy to data attributes

PHPBB3-15538
---
 phpBB/styles/prosilver/template/icons/iconify.html | 2 +-
 tests/template/extension_test.php                  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/phpBB/styles/prosilver/template/icons/iconify.html b/phpBB/styles/prosilver/template/icons/iconify.html
index 74820bafc0..480313faca 100644
--- a/phpBB/styles/prosilver/template/icons/iconify.html
+++ b/phpBB/styles/prosilver/template/icons/iconify.html
@@ -1,4 +1,4 @@
 {% spaceless %}
-	<i class="iconify o-icon {{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %}{{ ATTRIBUTES }}></i>
+	<i class="iconify o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="fa:{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
 	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
 {% endspaceless %}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index fec06fe296..a5af922c02 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -313,7 +313,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<i class="iconify o-icon phone"></i>',
+				'<i class="iconify o-icon" data-icon="fa:phone" data-inline="true"></i>',
 			],
 			/** Iconify: all options */
 			[
@@ -330,7 +330,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<i class="iconify o-icon pencil icon-lg" title="Pencil icon" aria-hidden="true" data-swap="Swap text"></i>
+				'<i class="iconify o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="fa:phone" data-inline="true" data-swap="Swap text"></i>
 				<span class="sr-only">Pencil icon</span>',
 			],
 			/** PNG: default */

From cf429fd8d005010ab6ad9b39ca25f699bf9e1ab6 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Sun, 10 Nov 2019 22:16:20 +0100
Subject: [PATCH 17/25] [ticket/15538] Remove fa: from iconify function

PHPBB3-15538
---
 phpBB/styles/prosilver/template/icons/iconify.html | 2 +-
 tests/template/extension_test.php                  | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/phpBB/styles/prosilver/template/icons/iconify.html b/phpBB/styles/prosilver/template/icons/iconify.html
index 480313faca..436999069e 100644
--- a/phpBB/styles/prosilver/template/icons/iconify.html
+++ b/phpBB/styles/prosilver/template/icons/iconify.html
@@ -1,4 +1,4 @@
 {% spaceless %}
-	<i class="iconify o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="fa:{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
+	<i class="iconify o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
 	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
 {% endspaceless %}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index a5af922c02..a9b2273372 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -306,7 +306,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 			[
 				[
 					'type'			=> 'iconify',
-					'icon'			=> 'phone',
+					'icon'			=> 'fa:phone',
 					'title'			=> '',
 					'hidden'		=> false,
 					'classes'		=> '',
@@ -319,7 +319,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 			[
 				[
 					'type'			=> 'iconify',
-					'icon'			=> 'pencil',
+					'icon'			=> 'mdi:pencil',
 					'title'			=> 'ICON_PENCIL',
 					'hidden'		=> true,
 					'classes'		=> 'icon-lg',
@@ -330,7 +330,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<i class="iconify o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="fa:phone" data-inline="true" data-swap="Swap text"></i>
+				'<i class="iconify o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="mdi:pencil" data-inline="true" data-swap="Swap text"></i>
 				<span class="sr-only">Pencil icon</span>',
 			],
 			/** PNG: default */

From 5bb6218606d1b4ce42c24bc7299ebde3a1785443 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Tue, 12 Nov 2019 11:02:28 +0100
Subject: [PATCH 18/25] [ticket/15538] Move style icon files to
 all/templates/macros/

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php                    | 2 +-
 .../template => all/templates/macros}/icons/font.html           | 0
 .../template => all/templates/macros}/icons/iconify.html        | 0
 .../{prosilver/template => all/templates/macros}/icons/png.html | 0
 .../{prosilver/template => all/templates/macros}/icons/svg.html | 0
 tests/template/extension_test.php                               | 2 +-
 6 files changed, 2 insertions(+), 2 deletions(-)
 rename phpBB/styles/{prosilver/template => all/templates/macros}/icons/font.html (100%)
 rename phpBB/styles/{prosilver/template => all/templates/macros}/icons/iconify.html (100%)
 rename phpBB/styles/{prosilver/template => all/templates/macros}/icons/png.html (100%)
 rename phpBB/styles/{prosilver/template => all/templates/macros}/icons/svg.html (100%)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index e3300eab80..065364a3e6 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -103,7 +103,7 @@ class icon extends \Twig\Extension\AbstractExtension
 
 		try
 		{
-			return $environment->render("icons/{$type}.html", [
+			return $environment->render("macros/icons/{$type}.html", [
 				'ATTRIBUTES'	=> (string) $this->implode_attributes($attributes),
 				'CLASSES'		=> (string) $classes,
 				'ICON'			=> (string) $icon,
diff --git a/phpBB/styles/prosilver/template/icons/font.html b/phpBB/styles/all/templates/macros/icons/font.html
similarity index 100%
rename from phpBB/styles/prosilver/template/icons/font.html
rename to phpBB/styles/all/templates/macros/icons/font.html
diff --git a/phpBB/styles/prosilver/template/icons/iconify.html b/phpBB/styles/all/templates/macros/icons/iconify.html
similarity index 100%
rename from phpBB/styles/prosilver/template/icons/iconify.html
rename to phpBB/styles/all/templates/macros/icons/iconify.html
diff --git a/phpBB/styles/prosilver/template/icons/png.html b/phpBB/styles/all/templates/macros/icons/png.html
similarity index 100%
rename from phpBB/styles/prosilver/template/icons/png.html
rename to phpBB/styles/all/templates/macros/icons/png.html
diff --git a/phpBB/styles/prosilver/template/icons/svg.html b/phpBB/styles/all/templates/macros/icons/svg.html
similarity index 100%
rename from phpBB/styles/prosilver/template/icons/svg.html
rename to phpBB/styles/all/templates/macros/icons/svg.html
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index a9b2273372..77cfa0ea82 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -109,7 +109,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 
 		$this->template->set_custom_style('tests', [
 			$this->template_path,
-			$phpbb_root_path . 'styles/prosilver/template',
+			$phpbb_root_path . 'styles/all/templates',
 		]);
 	}
 

From fa8cf86e638ea8cabebacfdb14ecbd39ebca0e1e Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Thu, 14 Nov 2019 23:49:33 +0100
Subject: [PATCH 19/25] [ticket/15538] Adjust classes to include o-icon-

PHPBB3-15538
---
 .../all/templates/macros/icons/font.html      |  2 +-
 .../all/templates/macros/icons/iconify.html   |  2 +-
 .../all/templates/macros/icons/png.html       |  2 +-
 .../all/templates/macros/icons/svg.html       |  2 +-
 tests/template/extension_test.php             | 20 +++++++++----------
 5 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/phpBB/styles/all/templates/macros/icons/font.html b/phpBB/styles/all/templates/macros/icons/font.html
index db43dd0d39..a882a25c40 100644
--- a/phpBB/styles/all/templates/macros/icons/font.html
+++ b/phpBB/styles/all/templates/macros/icons/font.html
@@ -1,4 +1,4 @@
 {% spaceless %}
-	<i class="o-icon font fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %}{{ ATTRIBUTES }}></i>
+	<i class="o-icon o-icon-font fa-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %}{{ ATTRIBUTES }}></i>
 	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
 {% endspaceless %}
diff --git a/phpBB/styles/all/templates/macros/icons/iconify.html b/phpBB/styles/all/templates/macros/icons/iconify.html
index 436999069e..6c3c269186 100644
--- a/phpBB/styles/all/templates/macros/icons/iconify.html
+++ b/phpBB/styles/all/templates/macros/icons/iconify.html
@@ -1,4 +1,4 @@
 {% spaceless %}
-	<i class="iconify o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
+	<i class="iconify o-icon-svg o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
 	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
 {% endspaceless %}
diff --git a/phpBB/styles/all/templates/macros/icons/png.html b/phpBB/styles/all/templates/macros/icons/png.html
index 48f8b81b88..426f7a3a5e 100644
--- a/phpBB/styles/all/templates/macros/icons/png.html
+++ b/phpBB/styles/all/templates/macros/icons/png.html
@@ -1,3 +1,3 @@
 {% spaceless %}
-	<img class="o-icon png png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" src="{{ SOURCE }}" alt="{{ lang(TITLE) }}"{{ ATTRIBUTES }} />
+	<img class="o-icon o-icon-png png-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" src="{{ SOURCE }}" alt="{{ lang(TITLE) }}"{{ ATTRIBUTES }} />
 {% endspaceless %}
diff --git a/phpBB/styles/all/templates/macros/icons/svg.html b/phpBB/styles/all/templates/macros/icons/svg.html
index 24a4459058..2bd69fd85a 100644
--- a/phpBB/styles/all/templates/macros/icons/svg.html
+++ b/phpBB/styles/all/templates/macros/icons/svg.html
@@ -1,7 +1,7 @@
 {% spaceless %}
 	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '_'}) ~ '-' ~ random() %}
 
-	<svg class="o-icon svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if TITLE %}{% if S_HIDDEN %} aria-hidden="true"{% endif %} aria-labelledby="{{ TITLE_ID }}"{% endif %} role="img"{{ ATTRIBUTES }}>
+	<svg class="o-icon o-icon-svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if TITLE %}{% if S_HIDDEN %} aria-hidden="true"{% endif %} aria-labelledby="{{ TITLE_ID }}"{% endif %} role="img"{{ ATTRIBUTES }}>
 		{% if TITLE %}<title id="{{ TITLE_ID }}">{{ lang(TITLE) }}</title>{% endif %}
 
 		{{ SOURCE }}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 77cfa0ea82..7c12a81842 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -280,7 +280,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PHONE'	=> 'Phone icon',
 				],
-				'<i class="o-icon font fa-phone"></i><span>Phone icon</span>',
+				'<i class="o-icon o-icon-font fa-phone"></i><span>Phone icon</span>',
 
 			],
 			/** Font: all options */
@@ -299,7 +299,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<i class="o-icon font fa-pencil a-class another-class" title="Pencil icon" aria-hidden="true" data-attr-1="true" data-attr-2="two"></i>
+				'<i class="o-icon o-icon-font fa-pencil a-class another-class" title="Pencil icon" aria-hidden="true" data-attr-1="true" data-attr-2="two"></i>
 				<span class="sr-only">Pencil icon</span>'
 			],
 			/** Iconify: default */
@@ -313,7 +313,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<i class="iconify o-icon" data-icon="fa:phone" data-inline="true"></i>',
+				'<i class="iconify o-icon-svg o-icon" data-icon="fa:phone" data-inline="true"></i>',
 			],
 			/** Iconify: all options */
 			[
@@ -330,7 +330,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<i class="iconify o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="mdi:pencil" data-inline="true" data-swap="Swap text"></i>
+				'<i class="iconify o-icon-svg o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="mdi:pencil" data-inline="true" data-swap="Swap text"></i>
 				<span class="sr-only">Pencil icon</span>',
 			],
 			/** PNG: default */
@@ -346,7 +346,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PHONE'	=> 'Phone icon',
 				],
-				'<img class="o-icon png png-phone" src="phpBB/styles/chameleon/theme/icons/png/phone.png" alt="Phone icon" />',
+				'<img class="o-icon o-icon-png png-phone" src="phpBB/styles/chameleon/theme/icons/png/phone.png" alt="Phone icon" />',
 			],
 			/** PNG: all options */
 			[
@@ -363,7 +363,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<img class="o-icon png png-pencil my-class" src="phpBB/styles/chameleon/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
+				'<img class="o-icon o-icon-png png-pencil my-class" src="phpBB/styles/chameleon/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
 			],
 			/** SVG: default */
 			[
@@ -378,7 +378,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PHONE'	=> 'Phone icon',
 				],
-				'<svg class="o-icon svg svg-phone" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-labelledby="icon_phone-123456789" role="img">
+				'<svg class="o-icon o-icon-svg svg-phone" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-labelledby="icon_phone-123456789" role="img">
 					<title id="icon_phone-123456789">Phone icon</title>
 					<path fill="none" d="M0 0h24v24H0z"></path>
 					<path d="M20.01 15.38c-1.23 0-2.42-.2-3.53-.56-.35-.12-.74-.03-1.01.24l-1.57 1.97c-2.83-1.35-5.48-3.9-6.89-6.83l1.95-1.66c.27-.28.35-.67.24-1.02-.37-1.11-.56-2.3-.56-3.53 0-.54-.45-.99-.99-.99H4.19C3.65 3 3 3.24 3 3.99 3 13.28 10.73 21 20.01 21c.71 0 .99-.63.99-1.18v-3.45c0-.54-.45-.99-.99-.99z"></path>
@@ -399,7 +399,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<svg class="o-icon svg svg-pencil my-svg-class" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" aria-labelledby="icon_pencil-123456789" role="img" data-ajax="my_ajax_callback">
+				'<svg class="o-icon o-icon-svg svg-pencil my-svg-class" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" aria-labelledby="icon_pencil-123456789" role="img" data-ajax="my_ajax_callback">
 					<title id="icon_pencil-123456789">Pencil icon</title>
 					<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path>
 					<path d="M0 0h24v24H0z" fill="none"></path>
@@ -416,7 +416,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<svg class="o-icon svg svg-dirty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img">
+				'<svg class="o-icon o-icon-svg svg-dirty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img">
 					<path fill-rule="evenodd" d="M139.05,117.4938 C139.05,129.1958 137.603,139.9498 134.718,149.7578 C131.832,159.5708 127.634,168.0768 122.129,175.2728 C116.623,182.4748 109.764,188.0558 101.554,192.0128 C93.344,195.9698 83.915,197.9538 73.267,197.9538 C65.142,197.9538 57.877,195.9278 51.473,191.8788 C45.064,187.8288 40.057,182.6558 36.45,176.3528 L36.45,240.6138 L25.194,240.6138 C21.976,240.6138 18.85,240.0268 15.811,238.8578 C12.774,237.6858 10.091,236.0228 7.77,233.8638 C5.446,231.7038 3.569,229.0918 2.144,226.0338 C0.713,222.9698 0,219.4608 0,215.5038 L0,127.4828 C0,115.4258 1.393,104.3558 4.185,94.2728 C6.974,84.1948 11.34,75.5548 17.28,68.3538 C23.22,61.1558 30.78,55.5748 39.96,51.6128 C49.14,47.6558 60.117,45.6728 72.9,45.6728 C82.62,45.6728 91.53,47.6978 99.63,51.7488 C107.729,55.7978 114.703,61.1558 120.555,67.8138 C126.402,74.4748 130.95,82.1238 134.19,90.7628 C137.43,99.4038 139.05,108.3128 139.05,117.4938 Z M101.79,126.9438 C101.79,109.6638 98.942,97.1548 93.247,89.4138 C87.552,81.6758 78.831,77.8028 67.087,77.8028 C56.966,77.8028 49.241,81.7178 43.909,89.5478 C38.576,97.3788 35.91,107.5958 35.91,120.1938 C35.91,134.7728 39.205,146.0248 45.803,153.9438 C52.401,161.8658 61.121,165.8238 71.968,165.8238 C80.823,165.8238 88.007,162.2708 93.521,155.1588 C99.031,148.0488 101.79,138.6458 101.79,126.9438 Z M267.5684,194.7134 C260.0084,194.7134 254.0224,192.6464 249.6134,188.5034 C245.2014,184.3644 242.9994,178.3364 242.9994,170.4134 L242.9994,111.0134 C242.9994,105.0734 242.2264,99.9894 240.6914,95.7584 C239.1554,91.5304 237.0754,88.1094 234.4514,85.4984 C231.8274,82.8914 228.7524,80.9544 225.2244,79.6934 C221.6984,78.4364 217.9434,77.8034 213.9644,77.8034 C211.0714,77.8034 208.0844,78.3894 205.0124,79.5584 C201.9374,80.7314 199.0894,82.6634 196.4654,85.3634 C193.8414,88.0634 191.7194,91.5734 190.0904,95.8934 C188.4634,100.2134 187.6484,105.6134 187.6484,112.0934 L187.6484,194.7134 L175.8574,194.7134 C167.2764,194.7134 161.0244,192.5534 157.0914,188.2334 C153.1604,183.9134 151.1984,177.9734 151.1984,170.4134 L151.1984,0.3134 L162.4544,0.3134 C171.0314,0.3134 177.4184,2.4734 181.6204,6.7934 C185.8174,11.1134 187.9194,16.6944 187.9194,23.5334 L188.1884,65.1134 C189.4454,62.9534 191.2014,60.7514 193.4534,58.4984 C195.7024,56.2504 198.1784,54.1784 200.8794,52.2884 C203.5784,50.3984 206.5484,48.8244 209.7894,47.5634 C213.0284,46.3064 216.3574,45.6734 219.7784,45.6734 C238.6784,45.6734 253.3474,51.1194 263.7894,62.0084 C274.2254,72.9014 279.4484,88.6954 279.4484,109.3934 L279.4484,194.7134 L267.5684,194.7134 Z M436.0442,117.4938 C436.0442,129.1958 434.5982,139.9498 431.7122,149.7578 C428.8272,159.5708 424.6282,168.0768 419.1242,175.2728 C413.6182,182.4748 406.7582,188.0558 398.5482,192.0128 C390.3392,195.9698 380.9102,197.9538 370.2622,197.9538 C362.1372,197.9538 354.8722,195.9278 348.4682,191.8788 C342.0592,187.8288 337.0522,182.6558 333.4442,176.3528 L333.4442,240.6138 L322.1882,240.6138 C318.9702,240.6138 315.8442,240.0268 312.8062,238.8578 C309.7682,237.6858 307.0862,236.0228 304.7652,233.8638 C302.4412,231.7038 300.5632,229.0918 299.1382,226.0338 C297.7082,222.9698 296.9942,219.4608 296.9942,215.5038 L296.9942,127.4828 C296.9942,115.4258 298.3872,104.3558 301.1802,94.2728 C303.9682,84.1948 308.3352,75.5548 314.2742,68.3538 C320.2152,61.1558 327.7742,55.5748 336.9542,51.6128 C346.1352,47.6558 357.1112,45.6728 369.8942,45.6728 C379.6142,45.6728 388.5242,47.6978 396.6252,51.7488 C404.7242,55.7978 411.6982,61.1558 417.5502,67.8138 C423.3972,74.4748 427.9442,82.1238 431.1842,90.7628 C434.4252,99.4038 436.0442,108.3128 436.0442,117.4938 Z M398.7842,126.9438 C398.7842,109.6638 395.9362,97.1548 390.2412,89.4138 C384.5462,81.6758 375.8262,77.8028 364.0812,77.8028 C353.9602,77.8028 346.2352,81.7178 340.9032,89.5478 C335.5712,97.3788 332.9042,107.5958 332.9042,120.1938 C332.9042,134.7728 336.1992,146.0248 342.7982,153.9438 C349.3952,161.8658 358.1162,165.8238 368.9622,165.8238 C377.8172,165.8238 385.0022,162.2708 390.5152,155.1588 C396.0252,148.0488 398.7842,138.6458 398.7842,126.9438 Z M581.5745,137.4732 C581.5745,146.8342 579.8615,155.1152 576.4445,162.3132 C573.0225,169.5152 568.3855,175.5892 562.5395,180.5382 C556.6875,185.4912 549.9375,189.2252 542.2895,191.7432 C534.6355,194.2662 526.5835,195.5232 518.1245,195.5232 L498.1435,195.5232 C463.7605,195.5232 446.5745,180.8552 446.5745,151.5132 L446.5745,17.3232 C448.7345,16.6062 451.7925,15.7502 455.7535,14.7582 C459.7105,13.7712 463.9895,12.8262 468.5795,11.9232 C473.1685,11.0252 477.8475,10.3032 482.6195,9.7632 C487.3855,9.2232 491.6645,8.9532 495.4445,8.9532 L517.8535,8.9532 C526.6705,8.9532 534.7715,10.0792 542.1545,12.3282 C549.5325,14.5812 555.9235,17.8212 561.3245,22.0482 C566.7235,26.2802 570.9095,31.4092 573.8785,37.4382 C576.8485,43.4712 578.3345,50.2632 578.3345,57.8232 C578.3345,68.0832 575.7225,76.5002 570.5035,83.0692 C565.2815,89.6412 558.0845,94.2732 548.9045,96.9732 C553.5835,98.2352 557.9025,100.3062 561.8645,103.1832 C565.8215,106.0652 569.2895,109.3512 572.2585,113.0382 C575.2285,116.7302 577.5245,120.7332 579.1435,125.0532 C580.7635,129.3732 581.5745,133.5162 581.5745,137.4732 Z M544.3135,138.5532 C544.3135,128.4752 541.7495,121.5432 536.6195,117.7632 C531.4895,113.9832 523.5245,112.0932 512.7235,112.0932 L483.2935,112.0932 L483.2935,150.1632 C483.2935,153.4042 484.8215,156.1502 487.8835,158.3982 C490.9425,160.6512 494.8115,161.7732 499.4945,161.7732 L514.6145,161.7732 C524.6925,161.7732 532.1645,159.6592 537.0245,155.4282 C541.8835,151.2012 544.3135,145.5742 544.3135,138.5532 Z M541.3435,61.0632 C541.3435,57.1062 540.4875,53.7312 538.7795,50.9382 C537.0665,48.1502 534.8645,45.9432 532.1645,44.3232 C529.4635,42.7032 526.4015,41.5342 522.9845,40.8132 C519.5625,40.0962 516.1415,39.7332 512.7235,39.7332 L498.9545,39.7332 C496.6125,39.7332 494.0005,39.9612 491.1245,40.4082 C488.2425,40.8602 485.5425,41.2642 483.0245,41.6232 L483.0245,83.4732 L511.3745,83.4732 C519.6515,83.4732 526.7175,81.7182 532.5695,78.2082 C538.4165,74.6982 541.3435,68.9862 541.3435,61.0632 Z M727.3733,137.4732 C727.3733,146.8342 725.6603,155.1152 722.2433,162.3132 C718.8213,169.5152 714.1843,175.5892 708.3383,180.5382 C702.4863,185.4912 695.7363,189.2252 688.0883,191.7432 C680.4343,194.2662 672.3823,195.5232 663.9233,195.5232 L643.9423,195.5232 C609.5593,195.5232 592.3733,180.8552 592.3733,151.5132 L592.3733,17.3232 C594.5333,16.6062 597.5913,15.7502 601.5523,14.7582 C605.5093,13.7712 609.7883,12.8262 614.3783,11.9232 C618.9673,11.0252 623.6463,10.3032 628.4183,9.7632 C633.1843,9.2232 637.4633,8.9532 641.2433,8.9532 L663.6523,8.9532 C672.4693,8.9532 680.5703,10.0792 687.9533,12.3282 C695.3313,14.5812 701.7223,17.8212 707.1233,22.0482 C712.5223,26.2802 716.7083,31.4092 719.6773,37.4382 C722.6473,43.4712 724.1333,50.2632 724.1333,57.8232 C724.1333,68.0832 721.5213,76.5002 716.3023,83.0692 C711.0803,89.6412 703.8833,94.2732 694.7033,96.9732 C699.3823,98.2352 703.7013,100.3062 707.6633,103.1832 C711.6203,106.0652 715.0883,109.3512 718.0573,113.0382 C721.0273,116.7302 723.3233,120.7332 724.9423,125.0532 C726.5623,129.3732 727.3733,133.5162 727.3733,137.4732 Z M690.1123,138.5532 C690.1123,128.4752 687.5483,121.5432 682.4183,117.7632 C677.2883,113.9832 669.3233,112.0932 658.5223,112.0932 L629.0923,112.0932 L629.0923,150.1632 C629.0923,153.4042 630.6203,156.1502 633.6823,158.3982 C636.7413,160.6512 640.6103,161.7732 645.2933,161.7732 L660.4133,161.7732 C670.4913,161.7732 677.9633,159.6592 682.8233,155.4282 C687.6823,151.2012 690.1123,145.5742 690.1123,138.5532 Z M687.1423,61.0632 C687.1423,57.1062 686.2863,53.7312 684.5783,50.9382 C682.8653,48.1502 680.6633,45.9432 677.9633,44.3232 C675.2623,42.7032 672.2003,41.5342 668.7833,40.8132 C665.3613,40.0962 661.9403,39.7332 658.5223,39.7332 L644.7533,39.7332 C642.4113,39.7332 639.7993,39.9612 636.9233,40.4082 C634.0413,40.8602 631.3413,41.2642 628.8233,41.6232 L628.8233,83.4732 L657.1733,83.4732 C665.4503,83.4732 672.5163,81.7182 678.3683,78.2082 C684.2153,74.6982 687.1423,68.9862 687.1423,61.0632z"></path>
 				</svg>',
 			],
@@ -430,7 +430,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 	{
 		$file = 'extension_icon_test.html';
 
-		$this->template->set_filenames(array('test' => $file));
+		$this->template->set_filenames(['test' => $file]);
 		$this->template->assign_vars($vars);
 
 		foreach ($lang_vars as $name => $value)

From 8bac1daba7263c00c6c9aa1bf467f2527f3f6b2e Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Mon, 18 Nov 2019 17:22:12 +0100
Subject: [PATCH 20/25] [ticket/15538] Add iconify source class

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php         | 11 ++++++++++-
 phpBB/styles/all/templates/macros/icons/iconify.html |  2 +-
 tests/template/extension_test.php                    |  6 +++---
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 065364a3e6..07b01eee4b 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -69,13 +69,22 @@ class icon extends \Twig\Extension\AbstractExtension
 	{
 		$type = strtolower($type);
 
+		if (empty($icon))
+		{
+			return '';
+		}
+
 		switch ($type)
 		{
 			case 'font':
-			case 'iconify':
 				$source = '';
 			break;
 
+			case 'iconify':
+				$source = explode(':', $icon);
+				$source = $source[0];
+			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();
diff --git a/phpBB/styles/all/templates/macros/icons/iconify.html b/phpBB/styles/all/templates/macros/icons/iconify.html
index 6c3c269186..43b8b88948 100644
--- a/phpBB/styles/all/templates/macros/icons/iconify.html
+++ b/phpBB/styles/all/templates/macros/icons/iconify.html
@@ -1,4 +1,4 @@
 {% spaceless %}
-	<i class="iconify o-icon-svg o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
+	<i class="iconify o-icon-src-{{ SOURCE }} o-icon{{ CLASSES ? ' ' ~ CLASSES }}"{% if S_HIDDEN %}{% if TITLE %} title="{{ lang(TITLE) }}"{% endif %} aria-hidden="true"{% endif %} data-icon="{{ ICON }}" data-inline="true"{{ ATTRIBUTES }}></i>
 	{% if TITLE %}<span{% if S_HIDDEN %} class="sr-only"{% endif %}>{{ lang(TITLE) }}</span>{% endif %}
 {% endspaceless %}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 7c12a81842..b46e83c7cd 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -313,7 +313,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<i class="iconify o-icon-svg o-icon" data-icon="fa:phone" data-inline="true"></i>',
+				'<i class="iconify o-icon-src-fa o-icon" data-icon="fa:phone" data-inline="true"></i>',
 			],
 			/** Iconify: all options */
 			[
@@ -324,13 +324,13 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'hidden'		=> true,
 					'classes'		=> 'icon-lg',
 					'attributes'	=> [
-						'data-swap'		=> 'Swap text',
+						'style'			=> 'color: #12a3eb;',
 					],
 				],
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<i class="iconify o-icon-svg o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="mdi:pencil" data-inline="true" data-swap="Swap text"></i>
+				'<i class="iconify o-icon-src-mdi o-icon icon-lg" title="Pencil icon" aria-hidden="true" data-icon="mdi:pencil" data-inline="true" style="color: #12a3eb;"></i>
 				<span class="sr-only">Pencil icon</span>',
 			],
 			/** PNG: default */

From 5a1be4611cfe5fe3b2c5b627cfc05479066f8afd Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Tue, 26 Nov 2019 15:17:47 +0100
Subject: [PATCH 21/25] [ticket/15538] Style inheritance for PNG and a default
 'no icon'

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php | 69 ++++++++++++++++++--
 tests/template/extension_test.php            | 38 ++++++++++-
 2 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 07b01eee4b..5376e88973 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -74,10 +74,13 @@ class icon extends \Twig\Extension\AbstractExtension
 			return '';
 		}
 
+		$not_found = false;
+		$source = '';
+
 		switch ($type)
 		{
 			case 'font':
-				$source = '';
+				// Nothing to do here..
 			break;
 
 			case 'iconify':
@@ -86,19 +89,39 @@ class icon extends \Twig\Extension\AbstractExtension
 			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();
-				$style_path	= $this->user->style['style_path'];
+				$filesystem	= $environment->get_filesystem();
+				$root_path	= $environment->get_web_root_path();
 
-				$source = "{$web_path}styles/{$style_path}/theme/icons/png/{$icon}.png";
+				$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/icons/png/{$icon}.png"))
+					{
+						$source = "{$base_path}styles/{$style_path}/theme/icons/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('icons/svg/' . $icon . '.svg');
 					$source	= $this->prepare_svg($file);
 				}
+				catch (\Twig\Error\LoaderError $e)
+				{
+					// Icon was not found
+					$not_found = true;
+				}
 				catch (\Twig\Error\Error $e)
 				{
 					return '';
@@ -110,6 +133,20 @@ class icon extends \Twig\Extension\AbstractExtension
 			break;
 		}
 
+		// If no PNG or SVG icon was found, display a default 404 PNG icon.
+		if ($not_found)
+		{
+			if (empty($base_path))
+			{
+				$board_url = defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH;
+				$base_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path();
+			}
+
+			$source = "{$base_path}styles/chameleon/theme/icons/png/404.png";
+			$type = 'png';
+			$icon = '404';
+		}
+
 		try
 		{
 			return $environment->render("macros/icons/{$type}.html", [
@@ -130,8 +167,11 @@ class icon extends \Twig\Extension\AbstractExtension
 	/**
 	 * Prepare an SVG for usage in the template icon.
 	 *
+	 * This removes any <?xml ?> and <!DOCTYPE> elements,
+	 * aswell as any <svg> and <title> elements.
+	 *
 	 * @param \Twig\TemplateWrapper	$file	The SVG file loaded from the environment
-	 * @return string
+	 * @return string						The cleaned SVG
 	 */
 	protected function prepare_svg(\Twig\TemplateWrapper $file)
 	{
@@ -195,4 +235,21 @@ class icon extends \Twig\Extension\AbstractExtension
 
 		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/tests/template/extension_test.php b/tests/template/extension_test.php
index b46e83c7cd..3a6421da7c 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -40,7 +40,15 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 		$auth->method('acl_get')
 			->willReturn(true);
 
-		$filesystem = new \phpbb\filesystem\filesystem();
+		$filesystem = $this->createMock('\phpbb\filesystem\filesystem');
+		$filesystem->expects($this->any())
+			->method('exists')
+			->with($this->stringContains('theme/icons/png/'))
+			->will($this->returnValueMap([
+				['phpBB/styles/chameleon/theme/icons/png/phone.png', true],
+				['phpBB/styles/chameleon/theme/icons/png/pencil.png', true],
+				['phpBB/styles/chameleon/theme/icons/png/user.png', false],
+			]));
 		$request = new phpbb_mock_request;
 		$symfony_request = new \phpbb\symfony_request(
 			$request
@@ -365,6 +373,21 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				],
 				'<img class="o-icon o-icon-png png-pencil my-class" src="phpBB/styles/chameleon/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
 			],
+			/** PNG: Not found */
+			[
+				[
+					'type'			=> 'png',
+					'icon'			=> 'user',
+					'title'			=> 'ICON_USER',
+					'hidden'		=> false,
+					'classes'		=> 'my-class',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_USER'		=> 'User icon',
+				],
+				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/chameleon/theme/icons/png/404.png" alt="User icon" />',
+			],
 			/** SVG: default */
 			[
 				[
@@ -405,6 +428,19 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					<path d="M0 0h24v24H0z" fill="none"></path>
 				</svg>',
 			],
+			/** SVG: Not found */
+			[
+				[
+					'type'			=> 'svg',
+					'icon'			=> 'not-existent',
+					'title'			=> 'Just a title',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[],
+				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/chameleon/theme/icons/png/404.png" alt="Just a title" />',
+			],
 			/** SVG: Sanitization */
 			[
 				[

From ac2b95c1fa486537bad9dc5a844ef4478914638b Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Wed, 27 Nov 2019 21:01:29 +0100
Subject: [PATCH 22/25] [ticket/15538] Update 404 source path to all/imgs

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php | 2 +-
 tests/template/extension_test.php            | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 5376e88973..92dcb38f3b 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -142,7 +142,7 @@ class icon extends \Twig\Extension\AbstractExtension
 				$base_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path();
 			}
 
-			$source = "{$base_path}styles/chameleon/theme/icons/png/404.png";
+			$source = "{$base_path}styles/all/imgs/icons/png/404.png";
 			$type = 'png';
 			$icon = '404';
 		}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 3a6421da7c..0b40eb9c38 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -386,7 +386,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_USER'		=> 'User icon',
 				],
-				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/chameleon/theme/icons/png/404.png" alt="User icon" />',
+				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/all/imgs/icons/png/404.png" alt="User icon" />',
 			],
 			/** SVG: default */
 			[
@@ -439,7 +439,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/chameleon/theme/icons/png/404.png" alt="Just a title" />',
+				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/all/imgs/icons/png/404.png" alt="Just a title" />',
 			],
 			/** SVG: Sanitization */
 			[

From c47f9d57444aff0ef8299db8d5fc4f5148f8ae35 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Sat, 30 Nov 2019 13:22:11 +0100
Subject: [PATCH 23/25] [ticket/15538] Drop templates and icons folder, twig
 file extension

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php   | 10 +++++-----
 .../macros/icons/font.twig}                    |  0
 .../macros/icons/iconify.twig}                 |  0
 .../macros/icons/png.twig}                     |  0
 .../macros/icons/svg.twig}                     |  0
 tests/template/extension_test.php              | 18 +++++++++---------
 .../templates/{icons => }/svg/dirty.svg        |  0
 .../templates/{icons => }/svg/pencil.svg       |  0
 .../templates/{icons => }/svg/phone.svg        |  0
 9 files changed, 14 insertions(+), 14 deletions(-)
 rename phpBB/styles/all/{templates/macros/icons/font.html => template/macros/icons/font.twig} (100%)
 rename phpBB/styles/all/{templates/macros/icons/iconify.html => template/macros/icons/iconify.twig} (100%)
 rename phpBB/styles/all/{templates/macros/icons/png.html => template/macros/icons/png.twig} (100%)
 rename phpBB/styles/all/{templates/macros/icons/svg.html => template/macros/icons/svg.twig} (100%)
 rename tests/template/templates/{icons => }/svg/dirty.svg (100%)
 rename tests/template/templates/{icons => }/svg/pencil.svg (100%)
 rename tests/template/templates/{icons => }/svg/phone.svg (100%)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 92dcb38f3b..c4a8781db3 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -98,9 +98,9 @@ class icon extends \Twig\Extension\AbstractExtension
 				// 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/icons/png/{$icon}.png"))
+					if ($filesystem->exists("{$root_path}styles/{$style_path}/theme/png/{$icon}.png"))
 					{
-						$source = "{$base_path}styles/{$style_path}/theme/icons/png/{$icon}.png";
+						$source = "{$base_path}styles/{$style_path}/theme/png/{$icon}.png";
 
 						break;
 					}
@@ -114,7 +114,7 @@ class icon extends \Twig\Extension\AbstractExtension
 				try
 				{
 					// Try to load and prepare the SVG icon
-					$file	= $environment->load('icons/svg/' . $icon . '.svg');
+					$file	= $environment->load('svg/' . $icon . '.svg');
 					$source	= $this->prepare_svg($file);
 				}
 				catch (\Twig\Error\LoaderError $e)
@@ -142,14 +142,14 @@ class icon extends \Twig\Extension\AbstractExtension
 				$base_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path();
 			}
 
-			$source = "{$base_path}styles/all/imgs/icons/png/404.png";
+			$source = "{$base_path}styles/all/imgs/png/404.png";
 			$type = 'png';
 			$icon = '404';
 		}
 
 		try
 		{
-			return $environment->render("macros/icons/{$type}.html", [
+			return $environment->render("macros/icons/{$type}.twig", [
 				'ATTRIBUTES'	=> (string) $this->implode_attributes($attributes),
 				'CLASSES'		=> (string) $classes,
 				'ICON'			=> (string) $icon,
diff --git a/phpBB/styles/all/templates/macros/icons/font.html b/phpBB/styles/all/template/macros/icons/font.twig
similarity index 100%
rename from phpBB/styles/all/templates/macros/icons/font.html
rename to phpBB/styles/all/template/macros/icons/font.twig
diff --git a/phpBB/styles/all/templates/macros/icons/iconify.html b/phpBB/styles/all/template/macros/icons/iconify.twig
similarity index 100%
rename from phpBB/styles/all/templates/macros/icons/iconify.html
rename to phpBB/styles/all/template/macros/icons/iconify.twig
diff --git a/phpBB/styles/all/templates/macros/icons/png.html b/phpBB/styles/all/template/macros/icons/png.twig
similarity index 100%
rename from phpBB/styles/all/templates/macros/icons/png.html
rename to phpBB/styles/all/template/macros/icons/png.twig
diff --git a/phpBB/styles/all/templates/macros/icons/svg.html b/phpBB/styles/all/template/macros/icons/svg.twig
similarity index 100%
rename from phpBB/styles/all/templates/macros/icons/svg.html
rename to phpBB/styles/all/template/macros/icons/svg.twig
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 0b40eb9c38..e6c53ac34d 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -43,11 +43,11 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 		$filesystem = $this->createMock('\phpbb\filesystem\filesystem');
 		$filesystem->expects($this->any())
 			->method('exists')
-			->with($this->stringContains('theme/icons/png/'))
+			->with($this->stringContains('theme/png/'))
 			->will($this->returnValueMap([
-				['phpBB/styles/chameleon/theme/icons/png/phone.png', true],
-				['phpBB/styles/chameleon/theme/icons/png/pencil.png', true],
-				['phpBB/styles/chameleon/theme/icons/png/user.png', false],
+				['phpBB/styles/chameleon/theme/png/phone.png', true],
+				['phpBB/styles/chameleon/theme/png/pencil.png', true],
+				['phpBB/styles/chameleon/theme/png/user.png', false],
 			]));
 		$request = new phpbb_mock_request;
 		$symfony_request = new \phpbb\symfony_request(
@@ -117,7 +117,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 
 		$this->template->set_custom_style('tests', [
 			$this->template_path,
-			$phpbb_root_path . 'styles/all/templates',
+			$phpbb_root_path . 'styles/all/template',
 		]);
 	}
 
@@ -354,7 +354,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PHONE'	=> 'Phone icon',
 				],
-				'<img class="o-icon o-icon-png png-phone" src="phpBB/styles/chameleon/theme/icons/png/phone.png" alt="Phone icon" />',
+				'<img class="o-icon o-icon-png png-phone" src="phpBB/styles/chameleon/theme/png/phone.png" alt="Phone icon" />',
 			],
 			/** PNG: all options */
 			[
@@ -371,7 +371,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_PENCIL'	=> 'Pencil icon',
 				],
-				'<img class="o-icon o-icon-png png-pencil my-class" src="phpBB/styles/chameleon/theme/icons/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
+				'<img class="o-icon o-icon-png png-pencil my-class" src="phpBB/styles/chameleon/theme/png/pencil.png" alt="Pencil icon" data-url="my-test-url/test-page.php?u=2" />',
 			],
 			/** PNG: Not found */
 			[
@@ -386,7 +386,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_USER'		=> 'User icon',
 				],
-				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/all/imgs/icons/png/404.png" alt="User icon" />',
+				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/all/imgs/png/404.png" alt="User icon" />',
 			],
 			/** SVG: default */
 			[
@@ -439,7 +439,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/all/imgs/icons/png/404.png" alt="Just a title" />',
+				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/all/imgs/png/404.png" alt="Just a title" />',
 			],
 			/** SVG: Sanitization */
 			[
diff --git a/tests/template/templates/icons/svg/dirty.svg b/tests/template/templates/svg/dirty.svg
similarity index 100%
rename from tests/template/templates/icons/svg/dirty.svg
rename to tests/template/templates/svg/dirty.svg
diff --git a/tests/template/templates/icons/svg/pencil.svg b/tests/template/templates/svg/pencil.svg
similarity index 100%
rename from tests/template/templates/icons/svg/pencil.svg
rename to tests/template/templates/svg/pencil.svg
diff --git a/tests/template/templates/icons/svg/phone.svg b/tests/template/templates/svg/phone.svg
similarity index 100%
rename from tests/template/templates/icons/svg/phone.svg
rename to tests/template/templates/svg/phone.svg

From 23a2c0c9c7d621cdbf4f1f20b04309d6783c9f85 Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Sat, 30 Nov 2019 15:01:25 +0100
Subject: [PATCH 24/25] [ticket/15538] 404 SVG and viewBox

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php  | 54 +++++++++++++------
 phpBB/styles/all/imgs/svg/404.svg             |  7 +++
 .../styles/all/template/macros/icons/svg.twig |  2 +-
 tests/template/extension_test.php             | 29 ++++++++--
 4 files changed, 69 insertions(+), 23 deletions(-)
 create mode 100644 phpBB/styles/all/imgs/svg/404.svg

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index c4a8781db3..5b9bb854a2 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -74,8 +74,9 @@ class icon extends \Twig\Extension\AbstractExtension
 			return '';
 		}
 
-		$not_found = false;
-		$source = '';
+		$not_found	= false;
+		$source		= '';
+		$view_box	= '';
 
 		switch ($type)
 		{
@@ -115,7 +116,12 @@ class icon extends \Twig\Extension\AbstractExtension
 				{
 					// Try to load and prepare the SVG icon
 					$file	= $environment->load('svg/' . $icon . '.svg');
-					$source	= $this->prepare_svg($file);
+					$source	= $this->prepare_svg($file, $view_box);
+
+					if (empty($view_box))
+					{
+						return '';
+					}
 				}
 				catch (\Twig\Error\LoaderError $e)
 				{
@@ -133,17 +139,13 @@ class icon extends \Twig\Extension\AbstractExtension
 			break;
 		}
 
-		// If no PNG or SVG icon was found, display a default 404 PNG icon.
+		// If no PNG or SVG icon was found, display a default 404 SVG icon.
 		if ($not_found)
 		{
-			if (empty($base_path))
-			{
-				$board_url = defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH;
-				$base_path = $board_url ? generate_board_url() . '/' : $environment->get_web_root_path();
-			}
+			$file	= $environment->load('svg/404.svg');
+			$source	= $this->prepare_svg($file, $view_box);
 
-			$source = "{$base_path}styles/all/imgs/png/404.png";
-			$type = 'png';
+			$type = 'svg';
 			$icon = '404';
 		}
 
@@ -155,6 +157,7 @@ class icon extends \Twig\Extension\AbstractExtension
 				'ICON'			=> (string) $icon,
 				'SOURCE'		=> (string) $source,
 				'TITLE'			=> (string) $title,
+				'VIEW_BOX'		=> (string) $view_box,
 				'S_HIDDEN'		=> (bool) $hidden,
 			]);
 		}
@@ -168,12 +171,13 @@ class icon extends \Twig\Extension\AbstractExtension
 	 * Prepare an SVG for usage in the template icon.
 	 *
 	 * This removes any <?xml ?> and <!DOCTYPE> elements,
-	 * aswell as any <svg> and <title> elements.
+	 * aswell as the root <svg> and any <title> elements.
 	 *
-	 * @param \Twig\TemplateWrapper	$file	The SVG file loaded from the environment
-	 * @return string						The cleaned SVG
+	 * @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)
+	protected function prepare_svg(\Twig\TemplateWrapper $file, &$view_box = '')
 	{
 		$code = $file->render();
 		$code = preg_replace( "/<\?xml.+?\?>/", '', $code);
@@ -198,11 +202,27 @@ class icon extends \Twig\Extension\AbstractExtension
 
 		$xpath = new \DOMXPath($doc);
 
-		// Remove all <svg> and <title> elements
-		foreach ($xpath->query('//svg | //title') as $element)
+		/**
+		 * 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);
diff --git a/phpBB/styles/all/imgs/svg/404.svg b/phpBB/styles/all/imgs/svg/404.svg
new file mode 100644
index 0000000000..a8bc69bba7
--- /dev/null
+++ b/phpBB/styles/all/imgs/svg/404.svg
@@ -0,0 +1,7 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
+  <g fill="none" fill-rule="evenodd">
+    <path fill="#D8D8D8" d="M0 0h512v512H0z"/>
+    <path fill="#979797" fill-rule="nonzero" d="M8 6.586l496 496v2.828L8 9.414z"/>
+    <path fill="#979797" fill-rule="nonzero" d="M504 7.586v2.828l-496 496v-2.828z"/>
+  </g>
+</svg>
diff --git a/phpBB/styles/all/template/macros/icons/svg.twig b/phpBB/styles/all/template/macros/icons/svg.twig
index 2bd69fd85a..e24777272e 100644
--- a/phpBB/styles/all/template/macros/icons/svg.twig
+++ b/phpBB/styles/all/template/macros/icons/svg.twig
@@ -1,7 +1,7 @@
 {% spaceless %}
 	{% set TITLE_ID = TITLE ? TITLE|lower|replace({' ': '_'}) ~ '-' ~ random() %}
 
-	<svg class="o-icon o-icon-svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"{% if TITLE %}{% if S_HIDDEN %} aria-hidden="true"{% endif %} aria-labelledby="{{ TITLE_ID }}"{% endif %} role="img"{{ ATTRIBUTES }}>
+	<svg class="o-icon o-icon-svg svg-{{ ICON ~ (CLASSES ? ' ' ~ CLASSES) }}" xmlns="http://www.w3.org/2000/svg" viewBox="{{ VIEW_BOX }}"{% if TITLE %}{% if S_HIDDEN %} aria-hidden="true"{% endif %} aria-labelledby="{{ TITLE_ID }}"{% endif %} role="img"{{ ATTRIBUTES }}>
 		{% if TITLE %}<title id="{{ TITLE_ID }}">{{ lang(TITLE) }}</title>{% endif %}
 
 		{{ SOURCE }}
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index e6c53ac34d..25624bc273 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -117,6 +117,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 
 		$this->template->set_custom_style('tests', [
 			$this->template_path,
+			$phpbb_root_path . 'styles/all/imgs',
 			$phpbb_root_path . 'styles/all/template',
 		]);
 	}
@@ -386,7 +387,14 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				[
 					'ICON_USER'		=> 'User icon',
 				],
-				'<img class="o-icon o-icon-png png-404 my-class" src="phpBB/styles/all/imgs/png/404.png" alt="User icon" />',
+				'<svg class="o-icon o-icon-svg svg-404 my-class" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-labelledby="icon_user-123456789" role="img">
+					<title id="icon_user-123456789">User icon</title>
+					<g fill="none" fill-rule="evenodd">
+						<path fill="#D8D8D8" d="M0 0h512v512H0z"></path>
+						<path fill="#979797" fill-rule="nonzero" d="M8 6.586l496 496v2.828L8 9.414z"></path>
+						<path fill="#979797" fill-rule="nonzero" d="M504 7.586v2.828l-496 496v-2.828z"></path>
+					</g>
+				</svg>',
 			],
 			/** SVG: default */
 			[
@@ -439,7 +447,14 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<img class="o-icon o-icon-png png-404" src="phpBB/styles/all/imgs/png/404.png" alt="Just a title" />',
+				'<svg class="o-icon o-icon-svg svg-404" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-labelledby="just_a_title-123456789" role="img">
+					<title id="just_a_title-123456789">Just a title</title>
+					<g fill="none" fill-rule="evenodd">
+						<path fill="#D8D8D8" d="M0 0h512v512H0z"></path>
+						<path fill="#979797" fill-rule="nonzero" d="M8 6.586l496 496v2.828L8 9.414z"></path>
+						<path fill="#979797" fill-rule="nonzero" d="M504 7.586v2.828l-496 496v-2.828z"></path>
+					</g>
+				</svg>',
 			],
 			/** SVG: Sanitization */
 			[
@@ -452,7 +467,7 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 					'attributes'	=> [],
 				],
 				[],
-				'<svg class="o-icon o-icon-svg svg-dirty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img">
+				'<svg class="o-icon o-icon-svg svg-dirty" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 728 242" role="img">
 					<path fill-rule="evenodd" d="M139.05,117.4938 C139.05,129.1958 137.603,139.9498 134.718,149.7578 C131.832,159.5708 127.634,168.0768 122.129,175.2728 C116.623,182.4748 109.764,188.0558 101.554,192.0128 C93.344,195.9698 83.915,197.9538 73.267,197.9538 C65.142,197.9538 57.877,195.9278 51.473,191.8788 C45.064,187.8288 40.057,182.6558 36.45,176.3528 L36.45,240.6138 L25.194,240.6138 C21.976,240.6138 18.85,240.0268 15.811,238.8578 C12.774,237.6858 10.091,236.0228 7.77,233.8638 C5.446,231.7038 3.569,229.0918 2.144,226.0338 C0.713,222.9698 0,219.4608 0,215.5038 L0,127.4828 C0,115.4258 1.393,104.3558 4.185,94.2728 C6.974,84.1948 11.34,75.5548 17.28,68.3538 C23.22,61.1558 30.78,55.5748 39.96,51.6128 C49.14,47.6558 60.117,45.6728 72.9,45.6728 C82.62,45.6728 91.53,47.6978 99.63,51.7488 C107.729,55.7978 114.703,61.1558 120.555,67.8138 C126.402,74.4748 130.95,82.1238 134.19,90.7628 C137.43,99.4038 139.05,108.3128 139.05,117.4938 Z M101.79,126.9438 C101.79,109.6638 98.942,97.1548 93.247,89.4138 C87.552,81.6758 78.831,77.8028 67.087,77.8028 C56.966,77.8028 49.241,81.7178 43.909,89.5478 C38.576,97.3788 35.91,107.5958 35.91,120.1938 C35.91,134.7728 39.205,146.0248 45.803,153.9438 C52.401,161.8658 61.121,165.8238 71.968,165.8238 C80.823,165.8238 88.007,162.2708 93.521,155.1588 C99.031,148.0488 101.79,138.6458 101.79,126.9438 Z M267.5684,194.7134 C260.0084,194.7134 254.0224,192.6464 249.6134,188.5034 C245.2014,184.3644 242.9994,178.3364 242.9994,170.4134 L242.9994,111.0134 C242.9994,105.0734 242.2264,99.9894 240.6914,95.7584 C239.1554,91.5304 237.0754,88.1094 234.4514,85.4984 C231.8274,82.8914 228.7524,80.9544 225.2244,79.6934 C221.6984,78.4364 217.9434,77.8034 213.9644,77.8034 C211.0714,77.8034 208.0844,78.3894 205.0124,79.5584 C201.9374,80.7314 199.0894,82.6634 196.4654,85.3634 C193.8414,88.0634 191.7194,91.5734 190.0904,95.8934 C188.4634,100.2134 187.6484,105.6134 187.6484,112.0934 L187.6484,194.7134 L175.8574,194.7134 C167.2764,194.7134 161.0244,192.5534 157.0914,188.2334 C153.1604,183.9134 151.1984,177.9734 151.1984,170.4134 L151.1984,0.3134 L162.4544,0.3134 C171.0314,0.3134 177.4184,2.4734 181.6204,6.7934 C185.8174,11.1134 187.9194,16.6944 187.9194,23.5334 L188.1884,65.1134 C189.4454,62.9534 191.2014,60.7514 193.4534,58.4984 C195.7024,56.2504 198.1784,54.1784 200.8794,52.2884 C203.5784,50.3984 206.5484,48.8244 209.7894,47.5634 C213.0284,46.3064 216.3574,45.6734 219.7784,45.6734 C238.6784,45.6734 253.3474,51.1194 263.7894,62.0084 C274.2254,72.9014 279.4484,88.6954 279.4484,109.3934 L279.4484,194.7134 L267.5684,194.7134 Z M436.0442,117.4938 C436.0442,129.1958 434.5982,139.9498 431.7122,149.7578 C428.8272,159.5708 424.6282,168.0768 419.1242,175.2728 C413.6182,182.4748 406.7582,188.0558 398.5482,192.0128 C390.3392,195.9698 380.9102,197.9538 370.2622,197.9538 C362.1372,197.9538 354.8722,195.9278 348.4682,191.8788 C342.0592,187.8288 337.0522,182.6558 333.4442,176.3528 L333.4442,240.6138 L322.1882,240.6138 C318.9702,240.6138 315.8442,240.0268 312.8062,238.8578 C309.7682,237.6858 307.0862,236.0228 304.7652,233.8638 C302.4412,231.7038 300.5632,229.0918 299.1382,226.0338 C297.7082,222.9698 296.9942,219.4608 296.9942,215.5038 L296.9942,127.4828 C296.9942,115.4258 298.3872,104.3558 301.1802,94.2728 C303.9682,84.1948 308.3352,75.5548 314.2742,68.3538 C320.2152,61.1558 327.7742,55.5748 336.9542,51.6128 C346.1352,47.6558 357.1112,45.6728 369.8942,45.6728 C379.6142,45.6728 388.5242,47.6978 396.6252,51.7488 C404.7242,55.7978 411.6982,61.1558 417.5502,67.8138 C423.3972,74.4748 427.9442,82.1238 431.1842,90.7628 C434.4252,99.4038 436.0442,108.3128 436.0442,117.4938 Z M398.7842,126.9438 C398.7842,109.6638 395.9362,97.1548 390.2412,89.4138 C384.5462,81.6758 375.8262,77.8028 364.0812,77.8028 C353.9602,77.8028 346.2352,81.7178 340.9032,89.5478 C335.5712,97.3788 332.9042,107.5958 332.9042,120.1938 C332.9042,134.7728 336.1992,146.0248 342.7982,153.9438 C349.3952,161.8658 358.1162,165.8238 368.9622,165.8238 C377.8172,165.8238 385.0022,162.2708 390.5152,155.1588 C396.0252,148.0488 398.7842,138.6458 398.7842,126.9438 Z M581.5745,137.4732 C581.5745,146.8342 579.8615,155.1152 576.4445,162.3132 C573.0225,169.5152 568.3855,175.5892 562.5395,180.5382 C556.6875,185.4912 549.9375,189.2252 542.2895,191.7432 C534.6355,194.2662 526.5835,195.5232 518.1245,195.5232 L498.1435,195.5232 C463.7605,195.5232 446.5745,180.8552 446.5745,151.5132 L446.5745,17.3232 C448.7345,16.6062 451.7925,15.7502 455.7535,14.7582 C459.7105,13.7712 463.9895,12.8262 468.5795,11.9232 C473.1685,11.0252 477.8475,10.3032 482.6195,9.7632 C487.3855,9.2232 491.6645,8.9532 495.4445,8.9532 L517.8535,8.9532 C526.6705,8.9532 534.7715,10.0792 542.1545,12.3282 C549.5325,14.5812 555.9235,17.8212 561.3245,22.0482 C566.7235,26.2802 570.9095,31.4092 573.8785,37.4382 C576.8485,43.4712 578.3345,50.2632 578.3345,57.8232 C578.3345,68.0832 575.7225,76.5002 570.5035,83.0692 C565.2815,89.6412 558.0845,94.2732 548.9045,96.9732 C553.5835,98.2352 557.9025,100.3062 561.8645,103.1832 C565.8215,106.0652 569.2895,109.3512 572.2585,113.0382 C575.2285,116.7302 577.5245,120.7332 579.1435,125.0532 C580.7635,129.3732 581.5745,133.5162 581.5745,137.4732 Z M544.3135,138.5532 C544.3135,128.4752 541.7495,121.5432 536.6195,117.7632 C531.4895,113.9832 523.5245,112.0932 512.7235,112.0932 L483.2935,112.0932 L483.2935,150.1632 C483.2935,153.4042 484.8215,156.1502 487.8835,158.3982 C490.9425,160.6512 494.8115,161.7732 499.4945,161.7732 L514.6145,161.7732 C524.6925,161.7732 532.1645,159.6592 537.0245,155.4282 C541.8835,151.2012 544.3135,145.5742 544.3135,138.5532 Z M541.3435,61.0632 C541.3435,57.1062 540.4875,53.7312 538.7795,50.9382 C537.0665,48.1502 534.8645,45.9432 532.1645,44.3232 C529.4635,42.7032 526.4015,41.5342 522.9845,40.8132 C519.5625,40.0962 516.1415,39.7332 512.7235,39.7332 L498.9545,39.7332 C496.6125,39.7332 494.0005,39.9612 491.1245,40.4082 C488.2425,40.8602 485.5425,41.2642 483.0245,41.6232 L483.0245,83.4732 L511.3745,83.4732 C519.6515,83.4732 526.7175,81.7182 532.5695,78.2082 C538.4165,74.6982 541.3435,68.9862 541.3435,61.0632 Z M727.3733,137.4732 C727.3733,146.8342 725.6603,155.1152 722.2433,162.3132 C718.8213,169.5152 714.1843,175.5892 708.3383,180.5382 C702.4863,185.4912 695.7363,189.2252 688.0883,191.7432 C680.4343,194.2662 672.3823,195.5232 663.9233,195.5232 L643.9423,195.5232 C609.5593,195.5232 592.3733,180.8552 592.3733,151.5132 L592.3733,17.3232 C594.5333,16.6062 597.5913,15.7502 601.5523,14.7582 C605.5093,13.7712 609.7883,12.8262 614.3783,11.9232 C618.9673,11.0252 623.6463,10.3032 628.4183,9.7632 C633.1843,9.2232 637.4633,8.9532 641.2433,8.9532 L663.6523,8.9532 C672.4693,8.9532 680.5703,10.0792 687.9533,12.3282 C695.3313,14.5812 701.7223,17.8212 707.1233,22.0482 C712.5223,26.2802 716.7083,31.4092 719.6773,37.4382 C722.6473,43.4712 724.1333,50.2632 724.1333,57.8232 C724.1333,68.0832 721.5213,76.5002 716.3023,83.0692 C711.0803,89.6412 703.8833,94.2732 694.7033,96.9732 C699.3823,98.2352 703.7013,100.3062 707.6633,103.1832 C711.6203,106.0652 715.0883,109.3512 718.0573,113.0382 C721.0273,116.7302 723.3233,120.7332 724.9423,125.0532 C726.5623,129.3732 727.3733,133.5162 727.3733,137.4732 Z M690.1123,138.5532 C690.1123,128.4752 687.5483,121.5432 682.4183,117.7632 C677.2883,113.9832 669.3233,112.0932 658.5223,112.0932 L629.0923,112.0932 L629.0923,150.1632 C629.0923,153.4042 630.6203,156.1502 633.6823,158.3982 C636.7413,160.6512 640.6103,161.7732 645.2933,161.7732 L660.4133,161.7732 C670.4913,161.7732 677.9633,159.6592 682.8233,155.4282 C687.6823,151.2012 690.1123,145.5742 690.1123,138.5532 Z M687.1423,61.0632 C687.1423,57.1062 686.2863,53.7312 684.5783,50.9382 C682.8653,48.1502 680.6633,45.9432 677.9633,44.3232 C675.2623,42.7032 672.2003,41.5342 668.7833,40.8132 C665.3613,40.0962 661.9403,39.7332 658.5223,39.7332 L644.7533,39.7332 C642.4113,39.7332 639.7993,39.9612 636.9233,40.4082 C634.0413,40.8602 631.3413,41.2642 628.8233,41.6232 L628.8233,83.4732 L657.1733,83.4732 C665.4503,83.4732 672.5163,81.7182 678.3683,78.2082 C684.2153,74.6982 687.1423,68.9862 687.1423,61.0632z"></path>
 				</svg>',
 			],
@@ -480,9 +495,13 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 		$expected = str_replace(["\n", "\r", "\t"], '', $expected);
 		$output = str_replace(["\n", "\r", "\t"], '', $this->display('test'));
 
-		if ($vars['type'] === 'svg')
+		/**
+		 * SVGs need their random identifier replaced (normalized).
+		 * The 'user' is a PNG, but not existent, so it returns a 404 SVG.
+		 */
+		if ($vars['type'] === 'svg' || $vars['icon'] === 'user')
 		{
-			$prefix = strtolower($vars['title']) . '-';
+			$prefix = strtolower(str_replace(' ', '_', $vars['title'])) . '-';
 			$output = preg_replace('/' . $prefix . '\d+/', $prefix . '123456789', $output);
 		}
 

From 811fbbeb922694c13c782567a5a5f294232acecd Mon Sep 17 00:00:00 2001
From: mrgoldy <gijsmartens1@gmail.com>
Date: Sat, 30 Nov 2019 15:55:21 +0100
Subject: [PATCH 25/25] [ticket/15538] Allow array of icons: icon_name =>
 boolean

PHPBB3-15538
---
 phpBB/phpbb/template/twig/extension/icon.php | 50 +++++++++++++++++++-
 tests/template/extension_test.php            | 44 +++++++++++++++++
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/phpBB/phpbb/template/twig/extension/icon.php b/phpBB/phpbb/template/twig/extension/icon.php
index 5b9bb854a2..f96ed94821 100644
--- a/phpBB/phpbb/template/twig/extension/icon.php
+++ b/phpBB/phpbb/template/twig/extension/icon.php
@@ -68,6 +68,7 @@ class icon extends \Twig\Extension\AbstractExtension
 	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))
 		{
@@ -142,8 +143,15 @@ class icon extends \Twig\Extension\AbstractExtension
 		// If no PNG or SVG icon was found, display a default 404 SVG icon.
 		if ($not_found)
 		{
-			$file	= $environment->load('svg/404.svg');
-			$source	= $this->prepare_svg($file, $view_box);
+			try
+			{
+				$file	= $environment->load('svg/404.svg');
+				$source	= $this->prepare_svg($file, $view_box);
+			}
+			catch (\Twig\Error\Error $e)
+			{
+				return '';
+			}
 
 			$type = 'svg';
 			$icon = '404';
@@ -238,6 +246,44 @@ class icon extends \Twig\Extension\AbstractExtension
 		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.
 	 *
diff --git a/tests/template/extension_test.php b/tests/template/extension_test.php
index 25624bc273..5d73aed91e 100644
--- a/tests/template/extension_test.php
+++ b/tests/template/extension_test.php
@@ -311,6 +311,50 @@ class phpbb_template_extension_test extends phpbb_template_template_test_case
 				'<i class="o-icon o-icon-font fa-pencil a-class another-class" title="Pencil icon" aria-hidden="true" data-attr-1="true" data-attr-2="two"></i>
 				<span class="sr-only">Pencil icon</span>'
 			],
+			/** Font: icons array */
+			[
+				[
+					'type'			=> 'font',
+					'icon'			=> [
+						'bullhorn'		=> false,
+						'star'			=> false,
+						'lock'			=> true,
+						'fire'			=> false,
+						'file'			=> true,
+					],
+					'title'			=> 'ICON_TOPIC',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_TOPIC'	=> 'Topic icon',
+				],
+				'<i class="o-icon o-icon-font fa-lock"></i>
+				<span>Topic icon</span>',
+			],
+			/** Font: icons array with no key for the default */
+			[
+				[
+					'type'			=> 'font',
+					'icon'			=> [
+						'bullhorn'		=> false,
+						'star'			=> false,
+						'lock'			=> false,
+						'fire'			=> false,
+						'file',
+					],
+					'title'			=> 'ICON_TOPIC',
+					'hidden'		=> false,
+					'classes'		=> '',
+					'attributes'	=> [],
+				],
+				[
+					'ICON_TOPIC'	=> 'Topic icon',
+				],
+				'<i class="o-icon o-icon-font fa-file"></i>
+				<span>Topic icon</span>',
+			],
 			/** Iconify: default */
 			[
 				[