From d13fcd44c7fbeb5bf98bfdb9e9dfb2a7bec5d49f Mon Sep 17 00:00:00 2001
From: Nick Liu <deltik@gmx.com>
Date: Sun, 12 Jan 2020 23:15:25 +0100
Subject: [PATCH 1/3] De-indented e107::url() with guard clause

Less cognitive complexity now that the e_url.php check is a guard clause
---
 e107_handlers/e107_class.php | 233 +++++++++++++++--------------------
 1 file changed, 97 insertions(+), 136 deletions(-)

diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php
index f2fffd146..8ca8e8eab 100755
--- a/e107_handlers/e107_class.php
+++ b/e107_handlers/e107_class.php
@@ -3403,18 +3403,16 @@ class e107
 	 * @param bool      $options['legacy'] When true legacy urls will be generated regardless of mod-rewrite status.
 	 * @return string
 	 */
-	public static function url($plugin='', $key=null, $row=array(), $options = array())
+	public static function url($plugin = '', $key = null, $row = array(), $options = array())
 	{
 
 		/* backward compat - core keys. ie. news/xxx/xxx user/xxx/xxx etc, */
-		$legacy = array('news','page','search','user','download','gallery');
+		$legacy = array('news', 'page', 'search', 'user', 'download', 'gallery');
 
-		if(strpos($plugin,'/')!==false)
-		{
-			$tmp = explode("/",$plugin,2);
+		if (strpos($plugin, '/') !== false) {
+			$tmp = explode("/", $plugin, 2);
 
-			if(in_array($tmp[0], $legacy))
-			{
+			if (in_array($tmp[0], $legacy)) {
 				return self::getUrl()->create($plugin, $key, $row);
 			}
 
@@ -3424,10 +3422,9 @@ class e107
 			$key = $tmp[1];
 		}
 
-		if(!$tmp = self::getRegistry('core/e107/addons/e_url'))
-		{
+		if (!$tmp = self::getRegistry('core/e107/addons/e_url')) {
 			$tmp = self::getUrlConfig();
-			self::setRegistry('core/e107/addons/e_url',$tmp);
+			self::setRegistry('core/e107/addons/e_url', $tmp);
 		}
 
 		$tp = self::getParser();
@@ -3437,7 +3434,7 @@ class e107
 		$rootNamespace = self::getPref('url_main_module');
 
 
-		if(is_string($options)) // backwards compat.
+		if (is_string($options)) // backwards compat.
 		{
 			$options = array(
 				'mode' => $options,
@@ -3446,159 +3443,123 @@ class e107
 
 		// Merge in defaults.
 		$options += array(
-			'mode'     => 'abs',
+			'mode' => 'abs',
 			'fragment' => '',
-			'query'    => array(),
+			'query' => array(),
 		);
 
-		if(isset($options['fragment']) && $options['fragment'] !== '')
-		{
+		if (isset($options['fragment']) && $options['fragment'] !== '') {
 			$options['fragment'] = '#' . $options['fragment'];
 		}
 
-		if(!empty($tmp[$plugin][$key]['sef']))
+		if (!empty($plugin) && empty($tmp[$plugin][$key]['sef'])) {
+			self::getMessage()->addDebug("e_url.php in <b>" . e_PLUGIN . $plugin . "</b> is missing the key: <b>" . $key . "</b>. Or, you may need to <a href='" . e_ADMIN . "db.php?mode=plugin_scan'>scan your plugin directories</a> to register e_url.php");
+			return false;
+		}
+
+		if (!empty($tmp[$plugin][$key]['alias'])) {
+			$alias = (!empty($pref[e_LAN][$plugin][$key])) ? $pref[e_LAN][$plugin][$key] : $tmp[$plugin][$key]['alias'];
+
+			if (!empty($rootNamespace) && $rootNamespace === $plugin) {
+				$replaceAlias = array('{alias}\/', '{alias}/');
+				$tmp[$plugin][$key]['sef'] = str_replace($replaceAlias, '', $tmp[$plugin][$key]['sef']);
+			} else {
+				$tmp[$plugin][$key]['sef'] = str_replace('{alias}', $alias, $tmp[$plugin][$key]['sef']);
+			}
+
+		}
+
+
+		preg_match_all('#{([a-z_]*)}#', $tmp[$plugin][$key]['sef'], $matches);
+
+		$active = true;
+
+		foreach ($matches[1] as $k => $v) // check if a field value is missing, if so, revent to legacy url.
 		{
-			if(!empty($tmp[$plugin][$key]['alias']))
-			{
-				$alias = (!empty($pref[e_LAN][$plugin][$key])) ? $pref[e_LAN][$plugin][$key] : $tmp[$plugin][$key]['alias'];
-
-				if(!empty($rootNamespace) && $rootNamespace === $plugin)
-				{
-					$replaceAlias = array('{alias}\/','{alias}/');
-					$tmp[$plugin][$key]['sef'] = str_replace($replaceAlias, '', $tmp[$plugin][$key]['sef']);
-				}
-				else
-				{
-					$tmp[$plugin][$key]['sef'] = str_replace('{alias}', $alias, $tmp[$plugin][$key]['sef']);
-				}
-
-			}
-
-
-			preg_match_all('#{([a-z_]*)}#', $tmp[$plugin][$key]['sef'],$matches);
-
-			$active = true;
-
-			foreach($matches[1] as $k=>$v) // check if a field value is missing, if so, revent to legacy url.
-			{
-				if(!isset($row[$v]))
-				{
-					self::getMessage()->addDebug("Missing value for ".$v." in ".$plugin."/e_url.php - '".$key."'");
-					$active = false;
-					break;
-				}
-			}
-
-			if(empty($sefActive[$plugin])) // SEF disabled.
-			{
-				self::getDebug()->log('SEF URL for <b>'.$plugin.'</b> disabled.');
+			if (!isset($row[$v])) {
+				self::getMessage()->addDebug("Missing value for " . $v . " in " . $plugin . "/e_url.php - '" . $key . "'");
 				$active = false;
+				break;
+			}
+		}
+
+		if (empty($sefActive[$plugin])) // SEF disabled.
+		{
+			self::getDebug()->log('SEF URL for <b>' . $plugin . '</b> disabled.');
+			$active = false;
+		}
+
+
+		if (deftrue('e_MOD_REWRITE') && ($active == true) && empty($options['legacy']))  // Search-Engine-Friendly URLs active.
+		{
+			$rawUrl = $tp->simpleParse($tmp[$plugin][$key]['sef'], $row);
+
+			if ($options['mode'] === 'full') {
+				$sefUrl = SITEURL . $rawUrl;
+			} elseif ($options['mode'] === 'raw') {
+				$sefUrl = $rawUrl;
+			} else {
+				$sefUrl = e_HTTP . $rawUrl;
 			}
 
-
-
-			if(deftrue('e_MOD_REWRITE') && ($active == true) && empty($options['legacy']))  // Search-Engine-Friendly URLs active.
-			{
-				$rawUrl = $tp->simpleParse($tmp[$plugin][$key]['sef'], $row);
-
-				if($options['mode'] === 'full')
-				{
-					$sefUrl = SITEURL.$rawUrl;
-				}
-				elseif($options['mode'] === 'raw')
-				{
-					$sefUrl = $rawUrl;
-				}
-				else
-				{
-					$sefUrl = e_HTTP.$rawUrl;
-				}
-
-				// Append the query.
-				if (is_array($options['query']) && !empty($options['query'])) {
-					$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
-				}
-
-				return $sefUrl . $options['fragment'];
+			// Append the query.
+			if (is_array($options['query']) && !empty($options['query'])) {
+				$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
 			}
-			else // Legacy URL.
-			{
 
-				$srch = array();
-				$repl = array();
+			return $sefUrl . $options['fragment'];
+		} else // Legacy URL.
+		{
 
-				foreach($matches[0] as $k=>$val)
-				{
-					$srch[] = '$'.($k+1);
-					$repl[] = $val;
-				}
+			$srch = array();
+			$repl = array();
 
-				$template = isset($tmp[$plugin][$key]['legacy']) ? $tmp[$plugin][$key]['legacy'] : $tmp[$plugin][$key]['redirect'];
+			foreach ($matches[0] as $k => $val) {
+				$srch[] = '$' . ($k + 1);
+				$repl[] = $val;
+			}
 
-				$urlTemplate = str_replace($srch,$repl, $template);
-				$urlTemplate = $tp->replaceConstants($urlTemplate, $options['mode']);
-				$legacyUrl = $tp->simpleParse($urlTemplate, $row);
+			$template = isset($tmp[$plugin][$key]['legacy']) ? $tmp[$plugin][$key]['legacy'] : $tmp[$plugin][$key]['redirect'];
 
-				$legacyUrl = preg_replace('/&?\$[\d]/i', "", $legacyUrl); // remove any left-over $x (including prefix of '&')
+			$urlTemplate = str_replace($srch, $repl, $template);
+			$urlTemplate = $tp->replaceConstants($urlTemplate, $options['mode']);
+			$legacyUrl = $tp->simpleParse($urlTemplate, $row);
+
+			$legacyUrl = preg_replace('/&?\$[\d]/i', "", $legacyUrl); // remove any left-over $x (including prefix of '&')
 
 
-				// Avoid duplicate query keys. eg. URL has ?id=x and $options['query']['id'] exists.
-				// @see forum/e_url.php - topic/redirect and forum/view_shortcodes.php sc_post_url()
-				list($legacyUrl,$tmp) = explode("?",$legacyUrl);
+			// Avoid duplicate query keys. eg. URL has ?id=x and $options['query']['id'] exists.
+			// @see forum/e_url.php - topic/redirect and forum/view_shortcodes.php sc_post_url()
+			list($legacyUrl, $tmp) = explode("?", $legacyUrl);
 
-				if(!empty($tmp))
-				{
-					if (strpos($tmp, '=') === false)
-					{
-						// required for legacy urls of type "request.php?download.43"
-						// @see: issue #3275
-						$legacyUrl .= '?' . $tmp;
-						$options['query'] = null;
-					}
-					else
-					{
+			if (!empty($tmp)) {
+				if (strpos($tmp, '=') === false) {
+					// required for legacy urls of type "request.php?download.43"
+					// @see: issue #3275
+					$legacyUrl .= '?' . $tmp;
+					$options['query'] = null;
+				} else {
 
-						parse_str($tmp,$qry);
+					parse_str($tmp, $qry);
 
-						foreach($qry as $k=>$v)
+					foreach ($qry as $k => $v) {
+						if (!isset($options['query'][$k])) // $options['query'] overrides any in the original URL.
 						{
-							if(!isset($options['query'][$k])) // $options['query'] overrides any in the original URL.
-							{
-								$options['query'][$k] = $v;
-							}
+							$options['query'][$k] = $v;
 						}
-
 					}
+
 				}
-
-				// Append the query.
-				if (is_array($options['query']) && !empty($options['query']))
-				{
-
-					$legacyUrl .= (strpos($legacyUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
-				}
-
-				return $legacyUrl . $options['fragment'];
 			}
 
+			// Append the query.
+			if (is_array($options['query']) && !empty($options['query'])) {
 
+				$legacyUrl .= (strpos($legacyUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
+			}
+
+			return $legacyUrl . $options['fragment'];
 		}
-
-		if(!empty($plugin))
-		{
-			self::getMessage()->addDebug("e_url.php in <b>".e_PLUGIN.$plugin."</b> is missing the key: <b>".$key."</b>. Or, you may need to <a href='".e_ADMIN."db.php?mode=plugin_scan'>scan your plugin directories</a> to register e_url.php");
-		}
-		return false;
-
-		/*
-		elseif(varset($tmp[$plugin][$key]['redirect']))
-		{
-			return self::getParser()->replaceConstants($tmp[$plugin][$key]['redirect'],'full');
-		}
-
-		return;
-		*/
-
 	}
 
 
@@ -3643,7 +3604,7 @@ class e107
 	 * rawurlencode() (instead of urlencode()) all query parameters.
 	 * @param array $query The query parameter array to be processed, e.g. $_GET.
 	 * @param string $parent Internal use only. Used to build the $query array key for nested items.
-	 * @return array A rawurlencoded string which can be used as or appended to the URL query string.
+	 * @return string A rawurlencoded string which can be used as or appended to the URL query string.
 	 */
 	public static function httpBuildQuery(array $query, $parent = '')
 	{

From e62422d63a409aa24b430551797e6b3cc9a8a761 Mon Sep 17 00:00:00 2001
From: Nick Liu <deltik@gmx.com>
Date: Mon, 13 Jan 2020 00:21:59 +0100
Subject: [PATCH 2/3] Old HTML ampersand strict compatibility

- FIX: e107::url() now puts "&amp;" in the query string instead of "&" for compliance with the older,
       looser definition of ambiguous ampersands in the HTML specification.
       Fixes: #4054
- FIX: Typo in comment
- FIX: Clear the core/e107/addons/e_url registry (cache) because if a plugin is installed after that
       cache is initialized, the cache is not updated anymore. The plugin's e_url is therefore not
       loaded, so SEF URLs won't be generated for that plugin until the cache is regenerated.
- NEW: Test for #4054
- FIX: e_pluginTest::testGetFields() expects the initial condition of the "forum" plugin to be
       uninstalled.
---
 e107_handlers/e107_class.php           |   28 +-
 e107_handlers/plugin_class.php         |    1 +
 e107_tests/tests/unit/e107Test.php     | 1973 ++++++++++++------------
 e107_tests/tests/unit/e_pluginTest.php |    1 +
 4 files changed, 1006 insertions(+), 997 deletions(-)

diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php
index 8ca8e8eab..2aa1b9fd3 100755
--- a/e107_handlers/e107_class.php
+++ b/e107_handlers/e107_class.php
@@ -3474,7 +3474,7 @@ class e107
 
 		$active = true;
 
-		foreach ($matches[1] as $k => $v) // check if a field value is missing, if so, revent to legacy url.
+		foreach ($matches[1] as $k => $v) // check if a field value is missing, if so, revert to legacy url.
 		{
 			if (!isset($row[$v])) {
 				self::getMessage()->addDebug("Missing value for " . $v . " in " . $plugin . "/e_url.php - '" . $key . "'");
@@ -3489,7 +3489,6 @@ class e107
 			$active = false;
 		}
 
-
 		if (deftrue('e_MOD_REWRITE') && ($active == true) && empty($options['legacy']))  // Search-Engine-Friendly URLs active.
 		{
 			$rawUrl = $tp->simpleParse($tmp[$plugin][$key]['sef'], $row);
@@ -3501,13 +3500,6 @@ class e107
 			} else {
 				$sefUrl = e_HTTP . $rawUrl;
 			}
-
-			// Append the query.
-			if (is_array($options['query']) && !empty($options['query'])) {
-				$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
-			}
-
-			return $sefUrl . $options['fragment'];
 		} else // Legacy URL.
 		{
 
@@ -3551,15 +3543,15 @@ class e107
 
 				}
 			}
-
-			// Append the query.
-			if (is_array($options['query']) && !empty($options['query'])) {
-
-				$legacyUrl .= (strpos($legacyUrl, '?') !== FALSE ? '&' : '?') . self::httpBuildQuery($options['query']);
-			}
-
-			return $legacyUrl . $options['fragment'];
+			$sefUrl = $legacyUrl;
 		}
+
+		// Append the query.
+		if (is_array($options['query']) && !empty($options['query'])) {
+			$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&amp;' : '?') . self::httpBuildQuery($options['query']);
+		}
+
+		return $sefUrl . $options['fragment'];
 	}
 
 
@@ -3631,7 +3623,7 @@ class e107
 			}
 		}
 
-		return implode('&', $params);
+		return implode('&amp;', $params);
 	}
 
 
diff --git a/e107_handlers/plugin_class.php b/e107_handlers/plugin_class.php
index f399d3d12..2a565c507 100644
--- a/e107_handlers/plugin_class.php
+++ b/e107_handlers/plugin_class.php
@@ -140,6 +140,7 @@ class e_plugin
 	{
 		$this->_installed = array();
 		$this->_addons = array();
+		e107::setRegistry('core/e107/addons/e_url');
 
 		$this->_init(true);
 		$this->_initIDs();
diff --git a/e107_tests/tests/unit/e107Test.php b/e107_tests/tests/unit/e107Test.php
index 7cffabcb7..23d7d3090 100644
--- a/e107_tests/tests/unit/e107Test.php
+++ b/e107_tests/tests/unit/e107Test.php
@@ -1,862 +1,877 @@
 <?php
+/**
+ * e107 website system
+ *
+ * Copyright (C) 2008-2018 e107 Inc (e107.org)
+ * Released under the terms and conditions of the
+ * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
+ *
+ */
+
+
+class e107Test extends \Codeception\Test\Unit
+{
+
+	/** @var e107 */
+	private $e107;
+
+	protected function _before()
+	{
+		try
+		{
+			$this->e107 = e107::getInstance();
+		}
+		catch (Exception $e)
+		{
+			$this->fail("Couldn't load e107 object");
+		}
+
+		// var_dump($this->e107);
+	}
+
+	public function testGetInstance()
+	{
+		//	$this->e107->getInstance();
+		//$res = $this->e107::getInstance();
+		//	$this->assertTrue($res);
+	}
+
+	public function testInitCore()
+	{
+		//$res = null;
+		include(APP_PATH.'/e107_config.php'); // contains $E107_CONFIG = array('site_path' => '000000test');
+
+		$e107_paths = compact('ADMIN_DIRECTORY', 'FILES_DIRECTORY', 'IMAGES_DIRECTORY', 'THEMES_DIRECTORY', 'PLUGINS_DIRECTORY', 'HANDLERS_DIRECTORY', 'LANGUAGES_DIRECTORY', 'HELP_DIRECTORY', 'DOWNLOADS_DIRECTORY','UPLOADS_DIRECTORY','SYSTEM_DIRECTORY', 'MEDIA_DIRECTORY','CACHE_DIRECTORY','LOGS_DIRECTORY', 'CORE_DIRECTORY', 'WEB_DIRECTORY');
+		$sql_info = compact('mySQLserver', 'mySQLuser', 'mySQLpassword', 'mySQLdefaultdb', 'mySQLprefix', 'mySQLport');
+		$res = $this->e107->initCore($e107_paths, e_ROOT, $sql_info, varset($E107_CONFIG, array()));
+
+		$this->assertEquals('000000test', $res->site_path);
+
+	}
+
+	/*
+			public function testInitInstall()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testMakeSiteHash()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSetDirs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testPrepareDirs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testDefaultDirs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testInitInstallSql()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetRegistry()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSetRegistry()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetFolder()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetE107()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIsCli()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetMySQLConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetSitePath()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetHandlerPath()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testAddHandler()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIsHandler()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetHandlerOverload()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSetHandlerOverload()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIsHandlerOverloadable()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetSingleton()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetObject()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testFindPref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPlugConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPlugLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPlugPref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testFindPlugPref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetThemeConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetThemePref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSetThemePref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetThemeGlyphs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetParser()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetScParser()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetSecureImg()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetScBatch()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetDb()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetCache()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetBB()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}*/
+
+	public function testGetUserSession()
+	{
+		$tmp = e107::getUserSession();
+
+		$className = get_class($tmp);
+
+		$res = ($className === 'UserHandler');
+
+		$this->assertTrue($res);
+
+	}
+	/*
+			public function testGetSession()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetRedirect()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetRate()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetSitelinks()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetRender()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetEmail()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetBulkEmail()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetEvent()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetArrayStorage()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetMenu()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetTheme()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUrl()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetFile()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetForm()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetAdminLog()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetLog()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetDateConvert()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetDate()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetDebug()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetNotify()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetOverride()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetLanguage()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetIPHandler()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetXml()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetHybridAuth()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUserClass()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetSystemUser()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testUser()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSerialize()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testUnserialize()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUser()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetModel()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUserStructure()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUserExt()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUserPerms()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetRank()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPlugin()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetPlug()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetOnline()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetChart()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetComment()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetCustomFields()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetMedia()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetNav()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetMessage()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetAjax()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetLibrary()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testLibrary()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetJs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testJs()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testLink()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testCss()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testDebug()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetJshelper()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testMeta()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetAdminUI()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetAddon()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetAddonConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testCallMethod()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetUrlConfig()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetThemeInfo()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testCoreTemplatePath()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testTemplatePath()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetCoreTemplate()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+	*/
 	/**
-	 * e107 website system
-	 *
-	 * Copyright (C) 2008-2018 e107 Inc (e107.org)
-	 * Released under the terms and conditions of the
-	 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
-	 *
+	 * This test checks getTemplate() use on loading between the core download plugin template and the _blank theme download template
 	 */
-
-
-	class e107Test extends \Codeception\Test\Unit
+	public function testGetTemplate()
 	{
 
-		/** @var e107 */
-		private $e107;
+		e107::getConfig()->set('sitetheme', '_blank');
 
-		protected function _before()
-		{
-			try
-			{
-				$this->e107 = e107::getInstance();
-			}
-			catch (Exception $e)
-			{
-				$this->fail("Couldn't load e107 object");
-			}
+		$template = e107::getTemplate('download', null, null); // theme override is enabled by default.
+		$this->assertEquals('{DOWNLOAD_BREADCRUMB} Custom', $template['header']); // ie. should be from _blank theme download template (override of plugin).
+		$footer = is_null($template['footer']); // theme overrides everything, since merge is not enabled. theme does not contain 'footer'.
+		$this->assertTrue($footer);
 
-			// var_dump($this->e107);
-		}
+		$template = e107::getTemplate('download', null, null, false); // theme override is disabled.
+		$this->assertEquals("{DOWNLOAD_BREADCRUMB}", $template['header']); // ie. should be from plugin template, not theme.
+		$this->assertEquals('', $template['footer']); // main plugin template is active, since override is false. 'footer' is set.
 
-		public function testGetInstance()
-		{
-		//	$this->e107->getInstance();
-			//$res = $this->e107::getInstance();
-		//	$this->assertTrue($res);
-		}
+		$template = e107::getTemplate('download', null, null, true, true); // theme override is enabled, and theme merge is enabled.
+		$this->assertEquals("{DOWNLOAD_BREADCRUMB} Custom", $template['header']); //from theme
+		$this->assertEquals("", $template['footer']); // 'footer' missing from theme, so plugin template used. ie. arrays have been merged.
 
-		public function testInitCore()
-		{
-			//$res = null;
-			include(APP_PATH.'/e107_config.php'); // contains $E107_CONFIG = array('site_path' => '000000test');
-
-			$e107_paths = compact('ADMIN_DIRECTORY', 'FILES_DIRECTORY', 'IMAGES_DIRECTORY', 'THEMES_DIRECTORY', 'PLUGINS_DIRECTORY', 'HANDLERS_DIRECTORY', 'LANGUAGES_DIRECTORY', 'HELP_DIRECTORY', 'DOWNLOADS_DIRECTORY','UPLOADS_DIRECTORY','SYSTEM_DIRECTORY', 'MEDIA_DIRECTORY','CACHE_DIRECTORY','LOGS_DIRECTORY', 'CORE_DIRECTORY', 'WEB_DIRECTORY');
-			$sql_info = compact('mySQLserver', 'mySQLuser', 'mySQLpassword', 'mySQLdefaultdb', 'mySQLprefix', 'mySQLport');
-			$res = $this->e107->initCore($e107_paths, e_ROOT, $sql_info, varset($E107_CONFIG, array()));
-
-			$this->assertEquals('000000test', $res->site_path);
-
-		}
-
-/*
-		public function testInitInstall()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testMakeSiteHash()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSetDirs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testPrepareDirs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testDefaultDirs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testInitInstallSql()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetRegistry()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSetRegistry()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetFolder()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetE107()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIsCli()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetMySQLConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetSitePath()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetHandlerPath()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testAddHandler()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIsHandler()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetHandlerOverload()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSetHandlerOverload()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIsHandlerOverloadable()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetSingleton()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetObject()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testFindPref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPlugConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPlugLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPlugPref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testFindPlugPref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetThemeConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetThemePref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSetThemePref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetThemeGlyphs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetParser()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetScParser()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetSecureImg()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetScBatch()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetDb()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetCache()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetBB()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}*/
-
-		public function testGetUserSession()
-		{
-			$tmp = e107::getUserSession();
-
-			$className = get_class($tmp);
-
-			$res = ($className === 'UserHandler');
-
-			$this->assertTrue($res);
-
-		}
-/*
-		public function testGetSession()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetRedirect()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetRate()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetSitelinks()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetRender()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetEmail()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetBulkEmail()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetEvent()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetArrayStorage()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetMenu()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetTheme()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUrl()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetFile()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetForm()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetAdminLog()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetLog()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetDateConvert()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetDate()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetDebug()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetNotify()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetOverride()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetLanguage()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetIPHandler()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetXml()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetHybridAuth()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUserClass()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetSystemUser()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testUser()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSerialize()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testUnserialize()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUser()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetModel()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUserStructure()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUserExt()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUserPerms()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetRank()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPlugin()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetPlug()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetOnline()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetChart()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetComment()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetCustomFields()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetMedia()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetNav()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetMessage()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetAjax()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetLibrary()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testLibrary()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetJs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testJs()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testLink()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testCss()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testDebug()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetJshelper()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testMeta()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetAdminUI()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetAddon()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetAddonConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testCallMethod()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetUrlConfig()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetThemeInfo()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testCoreTemplatePath()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testTemplatePath()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetCoreTemplate()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-*/
-		/**
-		 * This test checks getTemplate() use on loading between the core download plugin template and the _blank theme download template
-		 */
-		public function testGetTemplate()
-		{
-
-			e107::getConfig()->set('sitetheme', '_blank');
-
-			$template = e107::getTemplate('download', null, null); // theme override is enabled by default.
-			$this->assertEquals('{DOWNLOAD_BREADCRUMB} Custom', $template['header']); // ie. should be from _blank theme download template (override of plugin).
-			$footer = is_null($template['footer']); // theme overrides everything, since merge is not enabled. theme does not contain 'footer'.
-			$this->assertTrue($footer);
-
-			$template = e107::getTemplate('download', null, null, false); // theme override is disabled.
-			$this->assertEquals("{DOWNLOAD_BREADCRUMB}", $template['header']); // ie. should be from plugin template, not theme.
-			$this->assertEquals('', $template['footer']); // main plugin template is active, since override is false. 'footer' is set.
-
-			$template = e107::getTemplate('download', null, null, true, true); // theme override is enabled, and theme merge is enabled.
-			$this->assertEquals("{DOWNLOAD_BREADCRUMB} Custom", $template['header']); //from theme
-			$this->assertEquals("", $template['footer']); // 'footer' missing from theme, so plugin template used. ie. arrays have been merged.
-
-			$template = e107::getTemplate('download', null, null, false, true); // theme override is disabled, theme merge is enabled.
-			$this->assertEquals("{DOWNLOAD_BREADCRUMB}", $template['header']); // ie. should be from plugin template, not theme.
+		$template = e107::getTemplate('download', null, null, false, true); // theme override is disabled, theme merge is enabled.
+		$this->assertEquals("{DOWNLOAD_BREADCRUMB}", $template['header']); // ie. should be from plugin template, not theme.
 		//	$this->assertEquals("test", $template['other']); // 'test' is missing from plugin template, but merge is enabled. Not an override of plugin template key so merge is okay.
-			// FIXME above..
+		// FIXME above..
 		//	var_dump($template['other']);
 
-			e107::getConfig()->set('sitetheme', 'bootstrap3');
+		e107::getConfig()->set('sitetheme', 'bootstrap3');
 
 
-		}
-/*
-		public function testTemplateWrapper()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+	}
+	/*
+			public function testTemplateWrapper()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testScStyle()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testScStyle()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testGetTemplateInfo()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testGetTemplateInfo()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testGetLayouts()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testGetLayouts()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function test_getTemplate()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function test_getTemplate()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testIncludeLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testIncludeLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testCoreLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testCoreLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testPlugLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testPlugLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testThemeLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testThemeLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testLan()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
+			public function testLan()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
 
-		public function testPref()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-*/
-		public function testUrl()
-		{
-			$obj = $this->e107;
+			public function testPref()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+	*/
+	public function testUrl()
+	{
+		$obj = $this->e107;
 
-			$result = $obj::url('news','index', array(), array('mode'=>'full'));
+		$result = $obj::url('news','index', array(), array('mode'=>'full'));
 
-			$this->assertEquals("https://localhost/e107/news", $result);
-		//	var_dump(SITEURL);
+		$this->assertEquals("https://localhost/e107/news", $result);
+	}
+
+	/**
+	 * @see https://github.com/e107inc/e107/issues/4054
+	 */
+	public function testUrlOptionQueryHasCompliantAmpersand()
+	{
+		$e107 = $this->e107;
+		$e107::getPlugin()->install('forum');
+		$url = $e107::url('forum', 'topic', [], array(
+			'query' => array(
+				'f' => 'post',
+				'id' => 123
+			),
+		));
+		$this->assertEquals(
+			e_PLUGIN_ABS. 'forum/forum_viewtopic.php?f=post&amp;id=123',
+			$url, "Generated href does not match expectation"
+			);
+	}
+	/*
+			public function testRedirect()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetError()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testHttpBuildQuery()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testMinify()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testWysiwyg()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testLoadLanFiles()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testPrepare_request()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+	*/
+
+	public function testBase64DecodeOnAjaxURL()
+	{
+		$query = "mode=main&iframe=1&action=info&src=aWQ9ODgzJnVybD1odHRwcyUzQSUyRiUyRmUxMDcub3JnJTJGZTEwN19wbHVnaW5zJTJGYWRkb25zJTJGYWRkb25zLnBocCUzRmlkJTNEODgzJTI2YW1wJTNCbW9kYWwlM0QxJm1vZGU9YWRkb24mcHJpY2U9";
+
+		$result = base64_decode($query, true);
+
+		$this->assertFalse($result); // correct result is 'false'.
+	}
 
 
-		//	$this->assertTrue($res);
-		}
-/*
-		public function testRedirect()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetError()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testHttpBuildQuery()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testMinify()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testWysiwyg()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testLoadLanFiles()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testPrepare_request()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-*/
-
-		public function testBase64DecodeOnAjaxURL()
-		{
-			$query = "mode=main&iframe=1&action=info&src=aWQ9ODgzJnVybD1odHRwcyUzQSUyRiUyRmUxMDcub3JnJTJGZTEwN19wbHVnaW5zJTJGYWRkb25zJTJGYWRkb25zLnBocCUzRmlkJTNEODgzJTI2YW1wJTNCbW9kYWwlM0QxJm1vZGU9YWRkb24mcHJpY2U9";
-
-			$result = base64_decode($query, true);
-
-			$this->assertFalse($result); // correct result is 'false'.
-		}
-
-
-		public function testFilter_request()
-		{
+	public function testFilter_request()
+	{
 
 		//	define('e_DEBUG', true);
 		//	$_SERVER['QUEST_STRING'] = "mode=main&iframe=1&action=info&src=aWQ9ODgzJnVybD1odHRwcyUzQSUyRiUyRmUxMDcub3JnJTJGZTEwN19wbHVnaW5zJTJGYWRkb25zJTJGYWRkb25zLnBocCUzRmlkJTNEODgzJTI2YW1wJTNCbW9kYWwlM0QxJm1vZGU9YWRkb24mcHJpY2U9";
 
-			//$result = $this->e107::filter_request($test,'QUERY_STRING','_SERVER');
+		//$result = $this->e107::filter_request($test,'QUERY_STRING','_SERVER');
 
 		//	$this->e107->prepare_request();
 
@@ -864,156 +879,156 @@
 
 
 		// 	$res = null;
-			// $this->assertTrue($res);
-		}
-/*
-		public function testSet_base_path()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet_constants()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGet_override_rel()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGet_override_http()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet_paths()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testFix_windows_paths()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet_urls()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet_urls_deferred()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testSet_request()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testCanCache()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIsSecure()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGetip()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIpEncode()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testIpdecode()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testGet_host_name()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testParseMemorySize()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-*/
-		public function testIsInstalled()
-		{
-			$obj = $this->e107;
-
-			$result = $obj::isInstalled('user');
-
-			// var_dump($result);
-			$this->assertTrue($result);
-
-			$result = $obj::isInstalled('news');
-
-			// var_dump($result);
-			$this->assertTrue($result);
-		}
-/*
-		public function testIni_set()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testAutoload_register()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testAutoload()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function test__get()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testDestruct()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-		public function testCoreUpdateAvailable()
-		{
-			$res = null;
-			$this->assertTrue($res);
-		}
-
-
-*/
+		// $this->assertTrue($res);
 	}
+	/*
+			public function testSet_base_path()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet_constants()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGet_override_rel()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGet_override_http()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet_paths()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testFix_windows_paths()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet_urls()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet_urls_deferred()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testSet_request()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testCanCache()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIsSecure()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGetip()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIpEncode()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testIpdecode()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testGet_host_name()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testParseMemorySize()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+	*/
+	public function testIsInstalled()
+	{
+		$obj = $this->e107;
+
+		$result = $obj::isInstalled('user');
+
+		// var_dump($result);
+		$this->assertTrue($result);
+
+		$result = $obj::isInstalled('news');
+
+		// var_dump($result);
+		$this->assertTrue($result);
+	}
+	/*
+			public function testIni_set()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testAutoload_register()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testAutoload()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function test__get()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testDestruct()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+			public function testCoreUpdateAvailable()
+			{
+				$res = null;
+				$this->assertTrue($res);
+			}
+
+
+	*/
+}
diff --git a/e107_tests/tests/unit/e_pluginTest.php b/e107_tests/tests/unit/e_pluginTest.php
index 905d14875..93196e7b1 100644
--- a/e107_tests/tests/unit/e_pluginTest.php
+++ b/e107_tests/tests/unit/e_pluginTest.php
@@ -227,6 +227,7 @@
 
 		public function testGetFields()
 		{
+			e107::getPlugin()->uninstall('forum');
 			$result = $this->ep->clearCache()->load('forum')->getFields(true);
 
 		//	print_r($result);

From 4893ea7c67d451f9e08380a17ab6b4052ebd16c0 Mon Sep 17 00:00:00 2001
From: Nick Liu <deltik@gmx.com>
Date: Mon, 13 Jan 2020 01:04:10 +0100
Subject: [PATCH 3/3] Fix bad reformatting of e107::url()

---
 e107_handlers/e107_class.php | 61 +++++++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 19 deletions(-)

diff --git a/e107_handlers/e107_class.php b/e107_handlers/e107_class.php
index 2aa1b9fd3..320867707 100755
--- a/e107_handlers/e107_class.php
+++ b/e107_handlers/e107_class.php
@@ -3409,10 +3409,12 @@ class e107
 		/* backward compat - core keys. ie. news/xxx/xxx user/xxx/xxx etc, */
 		$legacy = array('news', 'page', 'search', 'user', 'download', 'gallery');
 
-		if (strpos($plugin, '/') !== false) {
+		if (strpos($plugin, '/') !== false)
+		{
 			$tmp = explode("/", $plugin, 2);
 
-			if (in_array($tmp[0], $legacy)) {
+			if (in_array($tmp[0], $legacy))
+			{
 				return self::getUrl()->create($plugin, $key, $row);
 			}
 
@@ -3422,7 +3424,8 @@ class e107
 			$key = $tmp[1];
 		}
 
-		if (!$tmp = self::getRegistry('core/e107/addons/e_url')) {
+		if (!$tmp = self::getRegistry('core/e107/addons/e_url'))
+		{
 			$tmp = self::getUrlConfig();
 			self::setRegistry('core/e107/addons/e_url', $tmp);
 		}
@@ -3448,22 +3451,28 @@ class e107
 			'query' => array(),
 		);
 
-		if (isset($options['fragment']) && $options['fragment'] !== '') {
+		if (isset($options['fragment']) && $options['fragment'] !== '')
+		{
 			$options['fragment'] = '#' . $options['fragment'];
 		}
 
-		if (!empty($plugin) && empty($tmp[$plugin][$key]['sef'])) {
+		if (!empty($plugin) && empty($tmp[$plugin][$key]['sef']))
+		{
 			self::getMessage()->addDebug("e_url.php in <b>" . e_PLUGIN . $plugin . "</b> is missing the key: <b>" . $key . "</b>. Or, you may need to <a href='" . e_ADMIN . "db.php?mode=plugin_scan'>scan your plugin directories</a> to register e_url.php");
 			return false;
 		}
 
-		if (!empty($tmp[$plugin][$key]['alias'])) {
+		if (!empty($tmp[$plugin][$key]['alias']))
+		{
 			$alias = (!empty($pref[e_LAN][$plugin][$key])) ? $pref[e_LAN][$plugin][$key] : $tmp[$plugin][$key]['alias'];
 
-			if (!empty($rootNamespace) && $rootNamespace === $plugin) {
+			if (!empty($rootNamespace) && $rootNamespace === $plugin)
+			{
 				$replaceAlias = array('{alias}\/', '{alias}/');
 				$tmp[$plugin][$key]['sef'] = str_replace($replaceAlias, '', $tmp[$plugin][$key]['sef']);
-			} else {
+			}
+			else
+			{
 				$tmp[$plugin][$key]['sef'] = str_replace('{alias}', $alias, $tmp[$plugin][$key]['sef']);
 			}
 
@@ -3476,7 +3485,8 @@ class e107
 
 		foreach ($matches[1] as $k => $v) // check if a field value is missing, if so, revert to legacy url.
 		{
-			if (!isset($row[$v])) {
+			if (!isset($row[$v]))
+			{
 				self::getMessage()->addDebug("Missing value for " . $v . " in " . $plugin . "/e_url.php - '" . $key . "'");
 				$active = false;
 				break;
@@ -3493,20 +3503,27 @@ class e107
 		{
 			$rawUrl = $tp->simpleParse($tmp[$plugin][$key]['sef'], $row);
 
-			if ($options['mode'] === 'full') {
+			if ($options['mode'] === 'full')
+			{
 				$sefUrl = SITEURL . $rawUrl;
-			} elseif ($options['mode'] === 'raw') {
+			}
+			elseif ($options['mode'] === 'raw')
+			{
 				$sefUrl = $rawUrl;
-			} else {
+			}
+			else
+			{
 				$sefUrl = e_HTTP . $rawUrl;
 			}
-		} else // Legacy URL.
+		}
+		else // Legacy URL.
 		{
 
 			$srch = array();
 			$repl = array();
 
-			foreach ($matches[0] as $k => $val) {
+			foreach ($matches[0] as $k => $val)
+			{
 				$srch[] = '$' . ($k + 1);
 				$repl[] = $val;
 			}
@@ -3524,17 +3541,22 @@ class e107
 			// @see forum/e_url.php - topic/redirect and forum/view_shortcodes.php sc_post_url()
 			list($legacyUrl, $tmp) = explode("?", $legacyUrl);
 
-			if (!empty($tmp)) {
-				if (strpos($tmp, '=') === false) {
+			if (!empty($tmp))
+			{
+				if (strpos($tmp, '=') === false)
+				{
 					// required for legacy urls of type "request.php?download.43"
 					// @see: issue #3275
 					$legacyUrl .= '?' . $tmp;
 					$options['query'] = null;
-				} else {
+				}
+				else
+				{
 
 					parse_str($tmp, $qry);
 
-					foreach ($qry as $k => $v) {
+					foreach ($qry as $k => $v)
+					{
 						if (!isset($options['query'][$k])) // $options['query'] overrides any in the original URL.
 						{
 							$options['query'][$k] = $v;
@@ -3547,7 +3569,8 @@ class e107
 		}
 
 		// Append the query.
-		if (is_array($options['query']) && !empty($options['query'])) {
+		if (is_array($options['query']) && !empty($options['query']))
+		{
 			$sefUrl .= (strpos($sefUrl, '?') !== FALSE ? '&amp;' : '?') . self::httpBuildQuery($options['query']);
 		}