635 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			635 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @package   FOF
 | |
|  * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 | |
|  * @license   GNU General Public License version 3, or later
 | |
|  */
 | |
| 
 | |
| namespace FOF40\Template;
 | |
| 
 | |
| defined('_JEXEC') || die;
 | |
| 
 | |
| use FOF40\Container\Container;
 | |
| use Joomla\CMS\Document\Document;
 | |
| use Joomla\CMS\Helper\ModuleHelper;
 | |
| use Joomla\CMS\HTML\HTMLHelper;
 | |
| use Joomla\CMS\Language\Text;
 | |
| use Joomla\CMS\Router\Route;
 | |
| use Joomla\CMS\Uri\Uri;
 | |
| use stdClass;
 | |
| 
 | |
| /**
 | |
|  * A utility class to load view templates, media files and modules.
 | |
|  *
 | |
|  * @since    1.0
 | |
|  */
 | |
| class Template
 | |
| {
 | |
| 	/**
 | |
| 	 * The component container
 | |
| 	 *
 | |
| 	 * @var  Container
 | |
| 	 */
 | |
| 	protected $container;
 | |
| 
 | |
| 	/**
 | |
| 	 * Public constructor
 | |
| 	 *
 | |
| 	 * @param   Container  $container  The component container
 | |
| 	 */
 | |
| 	function __construct(Container $container)
 | |
| 	{
 | |
| 		$this->container = $container;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add a CSS file to the page generated by the CMS
 | |
| 	 *
 | |
| 	 * @param   string       $uri      A path definition understood by parsePath, e.g. media://com_example/css/foo.css
 | |
| 	 * @param   string|null  $version  (optional) Version string to be added to the URL
 | |
| 	 * @param   string       $type     MIME type of the stylesheeet
 | |
| 	 * @param   string       $media    Media target definition of the style sheet, e.g. "screen"
 | |
| 	 * @param   array        $attribs  Array of attributes
 | |
| 	 *
 | |
| 	 * @see self::parsePath
 | |
| 	 *
 | |
| 	 * @return  void
 | |
| 	 */
 | |
| 	public function addCSS(string $uri, ?string $version = null, string $type = 'text/css', ?string $media = null, array $attribs = []): void
 | |
| 	{
 | |
| 		if ($this->container->platform->isCli())
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Make sure we have attributes
 | |
| 		if (empty($attribs) || !is_array($attribs))
 | |
| 		{
 | |
| 			$attribs = [];
 | |
| 		}
 | |
| 
 | |
| 		$url      = $this->parsePath($uri);
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		$options = [
 | |
| 			'version' => is_null($version) ? null : ((string) $version),
 | |
| 		];
 | |
| 
 | |
| 		$attribs['type'] = $type;
 | |
| 
 | |
| 		if (!empty($media))
 | |
| 		{
 | |
| 			$attribs['media'] = $media;
 | |
| 		}
 | |
| 
 | |
| 		$document->addStyleSheet($url, $options, $attribs);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add a JS script file to the page generated by the CMS.
 | |
| 	 *
 | |
| 	 * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp):
 | |
| 	 * * $defer false, $async true: The script is executed asynchronously with the rest of the page
 | |
| 	 *   (the script will be executed while the page continues the parsing)
 | |
| 	 * * $defer true, $async false: The script is executed when the page has finished parsing.
 | |
| 	 * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes
 | |
| 	 *   loading the browser continues parsing the rest of the page.
 | |
| 	 *
 | |
| 	 * When you are using $async = true there is no guarantee about the load order of the scripts. Whichever
 | |
| 	 * script loads first will be executed first. The order they appear on the page is completely irrelevant.
 | |
| 	 *
 | |
| 	 * If you set both async and defer to true it's the same as setting only async to true. That is to say, scripts will
 | |
| 	 * be loaded and executed in an order that is NOT guaranteed. Only set async for completely standalone scripts.
 | |
| 	 *
 | |
| 	 * If you are using defer you should pass script options using $this->container->platform->addScriptOptions(). This
 | |
| 	 * will add the script options as a JSON document in the document head. You can retrieve these options client-side
 | |
| 	 * with Joomla.getOptions() as long as the Joomla core JS has already been loaded, i.e. your deferred script is
 | |
| 	 * added AFTER Joomla's core code.
 | |
| 	 *
 | |
| 	 * @param   string       $uri      A path definition understood by parsePath, e.g. media://com_example/js/foo.js
 | |
| 	 * @param   boolean      $defer    Adds the defer attribute, see above
 | |
| 	 * @param   boolean      $async    Adds the async attribute, see above
 | |
| 	 * @param   string|null  $version  (optional) Version string to be added to the URL
 | |
| 	 * @param   string       $type     MIME type of the script
 | |
| 	 *
 | |
| 	 * @see self::parsePath
 | |
| 	 *
 | |
| 	 * @return  void
 | |
| 	 */
 | |
| 	public function addJS(string $uri, bool $defer = false, bool $async = false, ?string $version = null, string $type = 'text/javascript'): void
 | |
| 	{
 | |
| 		if ($this->container->platform->isCli())
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$url      = $this->container->template->parsePath($uri);
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		// Setting both defer and async is nonsense. Only async makes sense in this case.
 | |
| 		if ($defer && $async)
 | |
| 		{
 | |
| 			$defer = false;
 | |
| 		}
 | |
| 
 | |
| 		$options = [
 | |
| 			'version' => is_null($version) ? null : ((string) $version),
 | |
| 		];
 | |
| 
 | |
| 		$attribs['defer'] = $defer;
 | |
| 		$attribs['async'] = $async;
 | |
| 
 | |
| 		if (!empty($media))
 | |
| 		{
 | |
| 			$attribs['mime'] = $type;
 | |
| 		}
 | |
| 
 | |
| 		$document->addScript($url, $options, $attribs);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds an inline JavaScript script to the page header
 | |
| 	 *
 | |
| 	 * @param   string  $script  The script content to add
 | |
| 	 * @param   string  $type    The MIME type of the script
 | |
| 	 */
 | |
| 	public function addJSInline(string $script, string $type = 'text/javascript'): void
 | |
| 	{
 | |
| 		if ($this->container->platform->isCli())
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		if (!method_exists($document, 'addScriptDeclaration'))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$document->addScriptDeclaration($script, $type);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds an inline stylesheet (inline CSS) to the page header
 | |
| 	 *
 | |
| 	 * @param   string  $css   The stylesheet content to add
 | |
| 	 * @param   string  $type  The MIME type of the script
 | |
| 	 */
 | |
| 	public function addCSSInline(string $css, string $type = 'text/css'): void
 | |
| 	{
 | |
| 		if ($this->container->platform->isCli())
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		if (!method_exists($document, 'addStyleDeclaration'))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$document->addStyleDeclaration($css, $type);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Creates a SEF compatible sort header. Standard Joomla function will add a href="#" tag, so with SEF
 | |
| 	 * enabled, the browser will follow the fake link instead of processing the onSubmit event; so we
 | |
| 	 * need a fix.
 | |
| 	 *
 | |
| 	 * @param   string    $text   Header text
 | |
| 	 * @param   string    $field  Field used for sorting
 | |
| 	 * @param   stdClass  $list   Object holding the direction and the ordering field
 | |
| 	 *
 | |
| 	 * @return  string  HTML code for sorting
 | |
| 	 */
 | |
| 	public function sefSort(string $text, string $field, stdClass $list): string
 | |
| 	{
 | |
| 		$sort = HTMLHelper::_('grid.sort', Text::_(strtoupper($text)) . ' ', $field, $list->order_Dir, $list->order);
 | |
| 
 | |
| 		return str_replace('href="#"', 'href="javascript:void(0);"', $sort);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Parse a fancy path definition into a path relative to the site's root,
 | |
| 	 * respecting template overrides, suitable for inclusion of media files.
 | |
| 	 * For example, media://com_foobar/css/test.css is parsed into
 | |
| 	 * media/com_foobar/css/test.css if no override is found, or
 | |
| 	 * templates/mytemplate/media/com_foobar/css/test.css if the current
 | |
| 	 * template is called mytemplate and there's a media override for it.
 | |
| 	 *
 | |
| 	 * Regarding plugins, templates are searched inside the plugin's tmpl directory and the template's html directory.
 | |
| 	 * For instance considering plugin://system/example/something the files will be looked for in:
 | |
| 	 * plugins/system/example/tmpl/something.php
 | |
| 	 * templates/yourTemplate/html/plg_system_example/something.php
 | |
| 	 * Template paths for plugins are uncommon and not standard Joomla! practice. They make sense when you are
 | |
| 	 * implementing features of your component as plugins and they need to provide HTML output, e.g. some of the
 | |
| 	 * integration plugins we use in Akeeba Subscriptions.
 | |
| 	 *
 | |
| 	 * The valid protocols are:
 | |
| 	 *
 | |
| 	 *      media://        The media directory or a media override
 | |
| 	 *      plugin://    Given as plugin://pluginType/pluginName/template, e.g. plugin://system/example/something
 | |
| 	 *      admin://        Path relative to administrator directory (no overrides)
 | |
| 	 *      site://        Path relative to site's root (no overrides)
 | |
| 	 *      auto://      Automatically guess if it should be site:// or admin://
 | |
| 	 *      module://    The module directory or a template override (must be module://moduleName/templateName)
 | |
| 	 *
 | |
| 	 * @param   string   $path       Fancy path
 | |
| 	 * @param   boolean  $localFile  When true, it returns the local path, not the URL
 | |
| 	 *
 | |
| 	 * @return  string  Parsed path
 | |
| 	 *
 | |
| 	 * @see self::getAltPaths
 | |
| 	 *
 | |
| 	 */
 | |
| 	public function parsePath(string $path, bool $localFile = false): string
 | |
| 	{
 | |
| 		$separatorPosition = strpos($path, '://');
 | |
| 
 | |
| 		if ($separatorPosition === false)
 | |
| 		{
 | |
| 			return $path;
 | |
| 		}
 | |
| 
 | |
| 		$protocol = substr($path, 0, $separatorPosition);
 | |
| 
 | |
| 		switch ($protocol)
 | |
| 		{
 | |
| 			case 'media':
 | |
| 			case 'plugin':
 | |
| 			case 'module':
 | |
| 			case 'auto':
 | |
| 			case 'admin':
 | |
| 			case 'site':
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 				return $path;
 | |
| 		}
 | |
| 
 | |
| 		// Get the platform directories through the container
 | |
| 		$platformDirs = $this->container->platform->getPlatformBaseDirs();
 | |
| 
 | |
| 		$url = $localFile ? (rtrim($platformDirs['root'], DIRECTORY_SEPARATOR) . '/') : $this->container->platform->URIroot();
 | |
| 
 | |
| 		$altPaths = $this->getAltPaths($path);
 | |
| 		$filePath = $altPaths['normal'];
 | |
| 
 | |
| 		// If JDEBUG is enabled, prefer the debug path, else prefer an alternate path if present
 | |
| 		if (defined('JDEBUG') && JDEBUG && isset($altPaths['debug']))
 | |
| 		{
 | |
| 			if (file_exists($platformDirs['public'] . '/' . $altPaths['debug']))
 | |
| 			{
 | |
| 				$filePath = $altPaths['debug'];
 | |
| 			}
 | |
| 		}
 | |
| 		elseif (isset($altPaths['alternate']))
 | |
| 		{
 | |
| 			if (file_exists($platformDirs['public'] . '/' . $altPaths['alternate']))
 | |
| 			{
 | |
| 				$filePath = $altPaths['alternate'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $url . $filePath;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Parse a fancy path definition into a path relative to the site's root.
 | |
| 	 * It returns both the normal and alternative (template media override) path.
 | |
| 	 * For example, media://com_foobar/css/test.css is parsed into
 | |
| 	 * array(
 | |
| 	 *   'normal' => 'media/com_foobar/css/test.css',
 | |
| 	 *   'alternate' => 'templates/mytemplate/media/com_foobar/css//test.css'
 | |
| 	 * );
 | |
| 	 *
 | |
| 	 * The valid protocols are:
 | |
| 	 * media://        The media directory or a media override
 | |
| 	 * admin://        Path relative to administrator directory (no alternate)
 | |
| 	 * site://        Path relative to site's root (no alternate)
 | |
| 	 * auto://      Automatically guess if it should be site:// or admin://
 | |
| 	 * plugin://    The plugin directory or a template override (must be plugin://pluginType/pluginName/templateName)
 | |
| 	 * module://    The module directory or a template override (must be module://moduleName/templateName)
 | |
| 	 *
 | |
| 	 * @param   string  $path  Fancy path
 | |
| 	 *
 | |
| 	 * @return  array  Array of normal and alternate parsed path
 | |
| 	 */
 | |
| 	public function getAltPaths(string $path): array
 | |
| 	{
 | |
| 		$protoAndPath = explode('://', $path, 2);
 | |
| 
 | |
| 		if (count($protoAndPath) < 2)
 | |
| 		{
 | |
| 			$protocol = 'media';
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			$protocol = $protoAndPath[0];
 | |
| 			$path     = $protoAndPath[1];
 | |
| 		}
 | |
| 
 | |
| 		if ($protocol == 'auto')
 | |
| 		{
 | |
| 			$protocol = $this->container->platform->isBackend() ? 'admin' : 'site';
 | |
| 		}
 | |
| 
 | |
| 		$path = ltrim($path, '/' . DIRECTORY_SEPARATOR);
 | |
| 
 | |
| 		switch ($protocol)
 | |
| 		{
 | |
| 			case 'media':
 | |
| 				// Do we have a media override in the template?
 | |
| 				$pathAndParams = explode('?', $path, 2);
 | |
| 
 | |
| 				$ret = [
 | |
| 					'normal'    => 'media/' . $pathAndParams[0],
 | |
| 					'alternate' => $this->container->platform->getTemplateOverridePath('media:/' . $pathAndParams[0], false),
 | |
| 				];
 | |
| 				break;
 | |
| 
 | |
| 			case 'plugin':
 | |
| 				// The path is pluginType/pluginName/viewTemplate
 | |
| 				$pathInfo     = explode('/', $path);
 | |
| 				$pluginType   = $pathInfo[0] ?? 'system';
 | |
| 				$pluginName   = $pathInfo[1] ?? 'foobar';
 | |
| 				$viewTemplate = $pathInfo[2] ?? 'default';
 | |
| 
 | |
| 				$pluginSystemName = 'plg_' . $pluginType . '_' . $pluginName;
 | |
| 
 | |
| 				$ret = [
 | |
| 					'normal'    => 'plugins/' . $pluginType . '/' . $pluginName . '/tmpl/' . $viewTemplate,
 | |
| 					'alternate' => $this->container->platform->getTemplateOverridePath($pluginSystemName . '/' . $viewTemplate, false),
 | |
| 				];
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case 'module':
 | |
| 				// The path is moduleName/viewTemplate
 | |
| 				$pathInfo     = explode('/', $path, 2);
 | |
| 				$moduleName   = $pathInfo[0] ?? 'foobar';
 | |
| 				$viewTemplate = $pathInfo[1] ?? 'default';
 | |
| 
 | |
| 				$moduleSystemName = 'mod_' . $moduleName;
 | |
| 				$basePath         = $this->container->platform->isBackend() ? 'administrator/' : '';
 | |
| 
 | |
| 				$ret = [
 | |
| 					'normal'    => $basePath . 'modules/' . $moduleSystemName . '/tmpl/' . $viewTemplate,
 | |
| 					'alternate' => $this->container->platform->getTemplateOverridePath($moduleSystemName . '/' . $viewTemplate, false),
 | |
| 				];
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case 'admin':
 | |
| 				$ret = [
 | |
| 					'normal' => 'administrator/' . $path,
 | |
| 				];
 | |
| 				break;
 | |
| 
 | |
| 			default:
 | |
| 			case 'site':
 | |
| 				$ret = [
 | |
| 					'normal' => $path,
 | |
| 				];
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		// For CSS and JS files, add a debug path if the supplied file is compressed
 | |
| 		$filesystem = $this->container->filesystem;
 | |
| 		$ext        = $filesystem->getExt($ret['normal']);
 | |
| 
 | |
| 		if (in_array($ext, ['css', 'js']))
 | |
| 		{
 | |
| 			$file = basename($filesystem->stripExt($ret['normal']));
 | |
| 
 | |
| 			/*
 | |
| 			 * Detect if we received a file in the format name.min.ext
 | |
| 			 * If so, strip the .min part out, otherwise append -uncompressed
 | |
| 			 */
 | |
| 
 | |
| 			if (strlen($file) > 4 && strrpos($file, '.min', '-4'))
 | |
| 			{
 | |
| 				$position = strrpos($file, '.min', '-4');
 | |
| 				$filename = str_replace('.min', '.', $file, $position) . $ext;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$filename = $file . '-uncompressed.' . $ext;
 | |
| 			}
 | |
| 
 | |
| 			// Clone the $ret array so we can manipulate the 'normal' path a bit
 | |
| 			$t1   = (object) $ret;
 | |
| 			$temp = clone $t1;
 | |
| 			unset($t1);
 | |
| 			$temp       = (array) $temp;
 | |
| 			$normalPath = explode('/', $temp['normal']);
 | |
| 			array_pop($normalPath);
 | |
| 			$normalPath[] = $filename;
 | |
| 			$ret['debug'] = implode('/', $normalPath);
 | |
| 		}
 | |
| 
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Returns the contents of a module position
 | |
| 	 *
 | |
| 	 * @param   string  $position  The position name, e.g. "position-1"
 | |
| 	 * @param   int     $style     Rendering style, see ModuleRenderer::render
 | |
| 	 *
 | |
| 	 * @return  string  The contents of the module position
 | |
| 	 */
 | |
| 	public function loadPosition(string $position, int $style = -2): string
 | |
| 	{
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		if (!($document instanceof Document))
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		if (!method_exists($document, 'loadRenderer'))
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		try
 | |
| 		{
 | |
| 			$renderer = $document->loadRenderer('module');
 | |
| 		}
 | |
| 		catch (\Exception $exc)
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		$params = ['style' => $style];
 | |
| 
 | |
| 		$contents = '';
 | |
| 
 | |
| 		foreach (ModuleHelper::getModules($position) as $mod)
 | |
| 		{
 | |
| 			$contents .= $renderer->render($mod, $params);
 | |
| 		}
 | |
| 
 | |
| 		return $contents;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Render a module by name
 | |
| 	 *
 | |
| 	 * @param   string  $moduleName  The name of the module (real, eg 'Breadcrumbs' or folder, eg 'mod_breadcrumbs')
 | |
| 	 * @param   int     $style       The rendering style, see ModuleRenderer::render
 | |
| 	 *
 | |
| 	 * @return string  The rendered module
 | |
| 	 */
 | |
| 	public function loadModule(string $moduleName, int $style = -2): string
 | |
| 	{
 | |
| 		$document = $this->container->platform->getDocument();
 | |
| 
 | |
| 		if (!($document instanceof Document))
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		if (!method_exists($document, 'loadRenderer'))
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		try
 | |
| 		{
 | |
| 			$renderer = $document->loadRenderer('module');
 | |
| 		}
 | |
| 		catch (\Exception $exc)
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		$params = ['style' => $style];
 | |
| 
 | |
| 		$mod = ModuleHelper::getModule($moduleName);
 | |
| 
 | |
| 		if (empty($mod))
 | |
| 		{
 | |
| 			return '';
 | |
| 		}
 | |
| 
 | |
| 		return $renderer->render($mod, $params);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Performs SEF routing on a non-SEF URL, returning the SEF URL.
 | |
| 	 *
 | |
| 	 * When the $merge option is set to true the option, view, layout and format parameters of the current URL and the
 | |
| 	 * requested route will be merged. For example, assuming that current url is
 | |
| 	 * http://example.com/index.php?option=com_foo&view=cpanel
 | |
| 	 * then
 | |
| 	 * $template->route('view=categories&layout=tree');
 | |
| 	 * will result to
 | |
| 	 * http://example.com/index.php?option=com_foo&view=categories&layout=tree
 | |
| 	 *
 | |
| 	 * If $merge is unspecified (null) we will auto-detect the intended behavior. If you haven't specified option and
 | |
| 	 * one of view or task we will merge. Otherwise no merging takes place. This covers most use cases of FOF 3.0.
 | |
| 	 *
 | |
| 	 * @param   string  $route  The parameters string
 | |
| 	 * @param   bool    $merge  Should I perform parameter merging?
 | |
| 	 *
 | |
| 	 * @return  string  The human readable, complete url
 | |
| 	 */
 | |
| 	public function route(string $route = '', bool $merge = null): string
 | |
| 	{
 | |
| 		$route = trim($route);
 | |
| 
 | |
| 		/**
 | |
| 		 * Backwards compatibility with FOF 3.0: if the merge option is unspecified we will auto-detect the behaviour.
 | |
| 		 * If option and either one of view or task are present we won't merge.
 | |
| 		 */
 | |
| 		if (is_null($merge))
 | |
| 		{
 | |
| 			$hasOption = (strpos($route, 'option=') !== false);
 | |
| 			$hasView   = (strpos($route, 'view=') !== false);
 | |
| 			$hasTask   = (strpos($route, 'task=') !== false);
 | |
| 
 | |
| 			$merge = !($hasOption && ($hasView || $hasTask));
 | |
| 		}
 | |
| 
 | |
| 		if ($merge)
 | |
| 		{
 | |
| 			// Handle special cases before trying to merge
 | |
| 			if ($route == 'index.php' || $route == 'index.php?')
 | |
| 			{
 | |
| 				$result = 'index.php';
 | |
| 			}
 | |
| 			elseif (substr($route, 0, 1) == '&')
 | |
| 			{
 | |
| 				$url  = Uri::getInstance();
 | |
| 				$vars = [];
 | |
| 				parse_str($route, $vars);
 | |
| 
 | |
| 				$url->setQuery(array_merge($url->getQuery(true), $vars));
 | |
| 
 | |
| 				$result = 'index.php?' . $url->getQuery();
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				$url   = Uri::getInstance();
 | |
| 				$props = $url->getQuery(true);
 | |
| 
 | |
| 				// Strip 'index.php?'
 | |
| 				if (substr($route, 0, 10) == 'index.php?')
 | |
| 				{
 | |
| 					$route = substr($route, 10);
 | |
| 				}
 | |
| 
 | |
| 				// Parse route
 | |
| 				$parts = [];
 | |
| 				parse_str($route, $parts);
 | |
| 				$result = [];
 | |
| 
 | |
| 				// Check to see if there is component information in the route if not add it
 | |
| 
 | |
| 				if (!isset($parts['option']) && isset($props['option']))
 | |
| 				{
 | |
| 					$result[] = 'option=' . $props['option'];
 | |
| 				}
 | |
| 
 | |
| 				// Add the layout information to the route only if it's not 'default'
 | |
| 
 | |
| 				if (!isset($parts['view']) && isset($props['view']))
 | |
| 				{
 | |
| 					$result[] = 'view=' . $props['view'];
 | |
| 
 | |
| 					if (!isset($parts['layout']) && isset($props['layout']))
 | |
| 					{
 | |
| 						$result[] = 'layout=' . $props['layout'];
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				// Add the format information to the URL only if it's not 'html'
 | |
| 
 | |
| 				if (!isset($parts['format']) && isset($props['format']) && $props['format'] != 'html')
 | |
| 				{
 | |
| 					$result[] = 'format=' . $props['format'];
 | |
| 				}
 | |
| 
 | |
| 				// Reconstruct the route
 | |
| 
 | |
| 				if (!empty($route))
 | |
| 				{
 | |
| 					$result[] = $route;
 | |
| 				}
 | |
| 
 | |
| 				$result = 'index.php?' . implode('&', $result);
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			$result = $route;
 | |
| 		}
 | |
| 
 | |
| 		return Route::_($result);
 | |
| 	}
 | |
| }
 |