router.php 9.07 KB
<?php
// namespace components\com_jmap;
/**
 *
 * @package JMAP::components::com_jmap
 * @author Joomla! Extensions Store
 * @copyright (C) 2015 - Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html
 */
defined ( '_JEXEC' ) or die ( 'Restricted access' );

/**
 * Polyfill abstract class for class based router on Joomla 3.3-
 *
 * @package JMAP::components::com_jmap
 * @since 2.3
 */
if (! class_exists ( 'JComponentRouterBase' )) {
	abstract class JComponentRouterBase {
		abstract public function build(&$query);
		abstract public function parse(&$segments);
	}
}

/**
 * Routing class from com_jmap
 *
 * @package JMAP::components::com_jmap
 * @since 2.3
 */
class JMapRouter extends JComponentRouterBase {
	/**
	 * Sitemap Joomla router, embeds little helper route
	 *
	 * @package JMAP::components::com_jmap
	 */
	function build(&$query) {
		$config = JFactory::getConfig ();
		static $appSuffix;
		if ($appSuffix) {
			$config->set ( 'sef_suffix', $appSuffix );
		}
		$componentParams = JComponentHelper::getParams ( 'com_jmap' );
		
		// Segments that will be translated and built for this URL, subtracted from $query that will be untranslated and not built
		$segments = array ();
		
		$app = JFactory::getApplication ();
		// Get all site menus
		$menus = $app->getMenu ( 'site' );
		
		// Mapping fallback for generic task name = view name if view is not used. View concept is only used wrong way by Joomla menus
		if (! isset ( $query ['view'] ) && isset ( $query ['task'] )) {
			if (strpos ( $query ['task'], '.' )) {
				list ( $controller_name, $controller_task ) = explode ( '.', $query ['task'] );
			}
			$mappedView = $controller_name;
		}
		
		// Helper Route here for existing menu item pointing to this $query, so try finding Itemid before all
		if (empty ( $query ['Itemid'] ) && $app->getClientId () != 1) {
			$component = JComponentHelper::getComponent ( 'com_jmap' );
			$menuItems = $menus->getItems ( 'component_id', $component->id );
			if (! empty ( $menuItems )) {
				// Route helper priority 1: view + dataset
				if (isset ( $query ['dataset'] )) {
					foreach ( $menuItems as $menuItem ) {
						if (isset ( $menuItem->query ) && isset ( $menuItem->query ['dataset'] )) {
							if ($menuItem->query ['dataset'] == $query ['dataset']) {
								// Found a link exact match to sitemap view default html format within a site menu, use the Itemid for alias: component/com_jmap=>alias
								$query ['Itemid'] = $menuItem->id;
								break;
							}
						}
					}
				}
				
				// Route helper priority 2: view only
				if (empty ( $query ['Itemid'] )) {
					foreach ( $menuItems as $menuItem ) {
						if (isset ( $menuItem->query ['dataset'] ) && is_numeric ( $menuItem->query ['dataset'] )) {
							continue;
						}
						if (isset ( $menuItem->query ) && isset ( $menuItem->query ['view'] )) {
							if (isset ( $query ['view'] ) && $menuItem->query ['view'] == $query ['view']) {
								// Found a link exact match to sitemap view default html format within a site menu, use the Itemid for alias: component/com_jmap=>alias
								$query ['Itemid'] = $menuItem->id;
								break;
							}
							
							if (isset ( $mappedView ) && $menuItem->query ['view'] == $mappedView) {
								// Found a link exact match to sitemap view default html format within a site menu, use the Itemid for alias: component/com_jmap=>alias
								$query ['Itemid'] = $menuItem->id;
								break;
							}
						}
					}
				}
			}
		}
		
		// Lookup for an menu itemid in $query, should be helped by route helper if any, for mod_menu links there is always $query = http://domain.com/?Itemid=123, and all is desetted by default
		if (empty ( $query ['Itemid'] )) {
			$menuItem = $menus->getActive ();
		} else {
			$menuItem = $menus->getItem ( $query ['Itemid'] );
		}
		// Store query info for menu, for example view name, for the menu selected fom Itemid or current as fallback
		$mView = (empty ( $menuItem->query ['view'] )) ? null : $menuItem->query ['view'];
		$mFormat = (empty ( $menuItem->query ['format'] )) ? 'html' : $menuItem->query ['format'];
		$mDataset = (empty ( $menuItem->query ['dataset'] )) ? null : $menuItem->query ['dataset'];
		// If this is a link to HTML sitemap format view assigned already to a menu, ensure to unset all by default to leave only menu alias
		if (isset ( $query ['view'] ) && ($mView == $query ['view']) && (! isset ( $query ['format'] ) || $mFormat == $query ['format']) && (! isset ( $query ['dataset'] ) || $mDataset == $query ['dataset'])) {
			unset ( $query ['view'] );
			unset ( $query ['format'] );
			unset ( $query ['dataset'] );
			// Return empty segments ONLY if link has a view specified that match a menu item. Controller.task is always left as a segment because could have specific behavior
			return $segments;
		}
		
		// Start desetting $query chunks assigning to segments
		// UNSET VIEW
		if (isset ( $query ['view'] )) {
			// Store view info for $query link
			$view = $query ['view'];
			// Assign and unset
			$segments [] = $query ['view'];
			unset ( $query ['view'] );
		}
		
		// UNSET TASK
		if (isset ( $query ['task'] )) {
			// Assign and unset
			$segments [] = str_replace ( '.', '-', $query ['task'] );
			unset ( $query ['task'] );
		}
		
		// UNSET FORMAT
		if (isset ( $query ['format'] )) {
			// Assign and unset
			$segments [] = $query ['format'];
			unset ( $query ['format'] );
			
			// Manage XML/NOT HTML JDocument format if detected
			$appSuffix = $config->get ( 'sef_suffix' );
			$config->set ( 'sef_suffix', false );
			
			// Manage suffix for backend routing executing site application
			if ($app->getClientId () == 1 && $componentParams->get ( 'sitemap_links_sef', false )) {
				$siteApplication = JApplicationCms::getInstance ( 'site' );
				if (method_exists ( $siteApplication, 'set' )) {
					$siteApplication->set ( 'sef_suffix', false );
				}
			}
		}
		
		// UNSET XSLT
		$foundXslt = false;
		if (isset ( $query ['xslt'] )) {
			// Assign and unset
			$segments [] = $query ['xslt'] . '-formatted';
			unset ( $query ['xslt'] );
			$foundXslt = true;
		}
		
		// UNSET DATASET
		if (isset ( $query ['dataset'] )) {
			// Assign and unset
			if (! $foundXslt) {
				$segments [] = '0-formatted';
			}
			$segments [] = $query ['dataset'] . '-dataset';
			unset ( $query ['dataset'] );
		}
		
		// Finally return processed segments
		return $segments;
	}
	
	/**
	 * Parse the segments of a URL with following shapes:
	 *
	 * http://mydomain/component/jmap/view-task
	 *
	 * http://mydomain/component/jmap/viewname -> http://mydomain/component/jmap/viewname.display
	 * http://mydomain/component/jmap/controller-task
	 *
	 * component/jmap/ has to be handled through route helper for menu Itemid
	 * By convention view based Joomla components are overwritten by mapping viewname = taskname.display ex. view=sitemap is mapped to task=sitemap.display
	 * Even the task is considered and built/parsed fully as a dashed - entity like an article, a generic entity, etc: sitemap-display, sitemap-export AKA 1-myarticle
	 *
	 * @param
	 *        	array	The segments of the URL to parse.
	 * @return array URL attributes to be used by the application.
	 */
	function parse(&$segments) {
		$vars = array ();
		$count = count ( $segments );
		$app = JFactory::getApplication ();
		$componentParams = JComponentHelper::getParams ( 'com_jmap' );
		
		if ($count) {
			$count --;
			// VIEW-TASK is always 1 segment
			$segment = array_shift ( $segments );
			
			// Found a view/task
			if (version_compare ( JVERSION, '3.3', '>=' )) { // Fixed the semicolon/dash router issue?
				if (strpos ( $segment, '-' )) {
					$vars ['task'] = str_replace ( '-', '.', $segment );
				} else {
					$vars ['view'] = $segment;
				}
			} else { // Fallback on the compat mode 1:item
				if (strpos ( $segment, ':' )) {
					$vars ['task'] = str_replace ( ':', '.', $segment );
				} else {
					$vars ['view'] = $segment;
				}
			}
		}
		
		if ($count) {
			$count --;
			// FORMAT is always 2 segment
			$segment = array_shift ( $segments );
			if ($segment) {
				$vars ['format'] = $segment;
			}
		}
		
		if ($count) {
			$count --;
			// XSLT stylesheet is always 3 segment
			$segment = array_shift ( $segments );
			if (is_numeric ( ( int ) $segment )) {
				$vars ['xslt'] = ( int ) $segment;
			}
		}
		
		if ($count) {
			$count --;
			// Dataset is always 4 segment
			$segment = array_shift ( $segments );
			if (is_numeric ( ( int ) $segment )) {
				$vars ['dataset'] = ( int ) $segment;
			}
		}
		
		// Evaluate a forcing Itemid into vars and JMenu setter, link with no alias substitution, partially SEF from backend
		if (($ItemidByLink = $app->input->get ( 'Itemid', null )) && $componentParams->get ( 'sitemap_links_sef', false )) {
			$vars ['Itemid'] = $ItemidByLink;
			$menu = $app->getMenu ();
			$menu->setActive ( $vars ['Itemid'] );
		}
		
		return $vars;
	}
}

/**
 * JSitemap router functions
 *
 * These functions are proxys for the new router interface
 * for old SEF extensions.
 */
function JMapBuildRoute(&$query) {
	$router = new JMapRouter ();
	
	return $router->build ( $query );
}
function JMapParseRoute($segments) {
	$router = new JMapRouter ();
	
	return $router->parse ( $segments );
}