<?php
/**
* @version	$Id: url_manager.php 15389 2012-06-11 16:32:43Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2010 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class kUrlManager extends kBase {

	/**
	 * Processor of plain urls (initialized always)
	 *
	 * @var kPlainUrlProcessor
	 * @access public
	 */
	public $plain = null;

	/**
	 * Processor of rewritten urls (initialized on demand only)
	 *
	 * @var kRewriteUrlProcessor
	 * @access public
	 */
	public $rewrite = null;

	/**
	 * Tells, that rewrite system is ready
	 *
	 * @var bool
	 * @access protected
	 */
	protected $rewriteReady = false;

	/**
	 * Physical template name mapping to their template names based in "Structure & Data" section
	 *
	 * @var Array
	 */
	protected $structureTemplateMapping = Array ();

	/**
	 * Creates instead of kUrlManager class
	 */
	public function __construct()
	{
		parent::__construct();

		// don't use kApplication::recallObject, since it will call kApplication::EventManager, which isn't ready yet
		$this->plain = $this->Application->makeClass('kPlainUrlProcessor', Array (&$this));
	}

	/**
	 * Delay initialization of rewrite processor, since it uses site domains & http query
	 *
	 * @return void
	 * @access public
	 */
	public function initRewrite()
	{
		if ( $this->rewriteReady ) {
			return;
		}

		$this->rewrite = $this->Application->recallObject('kRewriteUrlProcessor', null, Array (), Array (&$this));
		$this->rewriteReady = true;
	}

	/**
	 * Return href for template
	 *
	 * @param string $t Template path
	 * @param string $prefix index.php prefix - could be blank, 'admin'
	 * @param Array $params
	 * @param string $index_file
	 * @return string
	 */
	public function HREF($t, $prefix = '', $params = Array (), $index_file = null)
	{
		static $current_theme_id = NULL;

		if ( !isset($current_theme_id) ) {
			$current_theme_id = $this->Application->GetVar('m_theme');
		}

		if ( !$t ) {
			// when template not specified, use current
			$t = $this->Application->GetVar('t');
		}

		$t = preg_replace('/^Content\//i', '', $t);

		if ( substr($t, -4) == '.tpl' ) {
			// cut template extension (deprecated link format)
			$t = substr($t, 0, strlen($t) - 4);
		}

		if ( substr($t, 0, 3) == 'id:' ) {
			// link to structure page using it's id
			$params['m_cat_id'] = substr($t, 3);
			$t = $this->structureTemplateMapping[$t];
		}

		if ( array_key_exists('use_section', $params) ) {
			$use_section = $params['use_section'];
			unset($params['use_section']);
		}

		$theme_id = isset($params['m_theme']) ? $params['m_theme'] : $current_theme_id;

		if ( isset($use_section) && $use_section && array_key_exists($t . ':' . $theme_id, $this->structureTemplateMapping) ) {
			// structure template corresponding to given physical template
			$t = $this->structureTemplateMapping[$t . ':' . $theme_id];
			unset($params['use_section']);
		}

		if ( preg_match('/external:(.*)/', $t, $regs) ) {
			// external url
			return $regs[1];
		}

		if ( $this->Application->isAdmin && $prefix == '' ) {
			$prefix = ADMIN_DIRECTORY;
		}

		if ( $this->Application->isAdmin && $prefix == '_FRONT_END_' ) {
			$prefix = '';
		}

		if ( isset($params['_auto_prefix_']) ) {
			unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here
		}

		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : NULL;
		if ( $ssl !== NULL ) {
			$session = $this->Application->recallObject('Session');
			/* @var $session Session */

			$target_url = rtrim($this->Application->BaseURL('', $ssl, false), '/');
			$cookie_url = trim($session->CookieDomain . $session->CookiePath, '/.');

			// set session to GET_ONLY, to pass sid only if sid is REAL AND session is set
			if ( !preg_match('#' . preg_quote($cookie_url) . '#', $target_url) && $session->SessionSet ) {
				// when SSL<->NON-SSL redirect to different domain pass SID in url
				$session->SetMode(Session::smGET_ONLY);
			}
		}

		if ( isset($params['opener']) && $params['opener'] == 'u' ) {
			$ret = $this->processPopupClose($prefix, $params);

			if ( $ret !== false ) {
				return $ret;
			}
			else {
				//define('DBG_REDIRECT', 1);
				$t = $this->Application->GetVar('t');
			}
		}

		$pass = isset($params['pass']) ? $params['pass'] : '';

		// pass events with url
		$pass_events = false;
		if ( isset($params['pass_events']) ) {
			$pass_events = $params['pass_events'];
			unset($params['pass_events']);
		}

		$map_link = '';
		if ( isset($params['anchor']) ) {
			$map_link = '#' . $params['anchor'];
			unset($params['anchor']);
		}

		if ( isset($params['no_amp']) ) {
			$params['__URLENCODE__'] = $params['no_amp'];
			unset($params['no_amp']);
		}

		$rewrite = true;
		if ( isset($params['__NO_REWRITE__']) ) {
			$rewrite = false;
			unset($params['__NO_REWRITE__']);
		}

		$force_rewrite = false;
		if ( isset($params['__MOD_REWRITE__']) ) {
			$force_rewrite = true;
			unset($params['__MOD_REWRITE__']);
		}

		$force_no_sid = false;
		if ( isset($params['__NO_SID__']) ) {
			$force_no_sid = true;
			unset($params['__NO_SID__']);
		}

		// append pass through variables to each link to be build
		$params = array_merge($this->getPassThroughVariables($params), $params);

		$session = $this->Application->recallObject('Session');

		if ( $session->NeedQueryString() && !$force_no_sid ) {
			$params['sid'] = $this->Application->GetSID();
		}

		if ( $force_rewrite || ($this->Application->RewriteURLs($ssl) && $rewrite) ) {
			if ( !$this->rewriteReady ) {
				$this->initRewrite();
			}

			$url = $this->rewrite->build($t, $params, $pass, $pass_events);
		}
		else {
			unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off

			$index_file = $this->getIndexFile($prefix, $index_file, $params);
			$url = $index_file . '?' . $this->plain->build($t, $params, $pass, $pass_events);
		}

		return $this->Application->BaseURL($prefix, $ssl) . $url . $map_link;
	}

	/**
	 * Returns popup's parent window url and optionally removes it from opener stack
	 *
	 * @param string $prefix
	 * @param Array $params
	 * @return bool|string
	 * @access protected
	 */
	protected function processPopupClose($prefix = '', $params = Array ())
	{
		$opener_stack = $this->Application->makeClass('kOpenerStack');
		/* @var $opener_stack kOpenerStack */

		if ( $opener_stack->isEmpty() ) {
			return false;
		}

		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
		list($index_file, $env) = explode('|', $opener_stack->get(kOpenerStack::LAST_ELEMENT, true));
		$ret = $this->Application->BaseURL($prefix, $ssl) . $index_file . '?' . ENV_VAR_NAME . '=' . $env;

		if ( isset($params['escape']) && $params['escape'] ) {
			$ret = addslashes($ret);
		}

		if ( isset($params['m_opener']) && $params['m_opener'] == 'u' ) {
			$opener_stack->pop();
			$opener_stack->save(true);

			if ( $opener_stack->isEmpty() ) {
				// remove popups last templates, because popup is closing now
				$this->Application->RemoveVar('last_template_' . $opener_stack->getWindowID());
				$this->Application->RemoveVar('last_template_popup_' . $opener_stack->getWindowID());

				// don't save popups last templates again :)
				$this->Application->SetVar('skip_last_template', 1);
			}

			// store window relations
			/*$window_relations = $this->Application->RecallVar('window_relations');
			$window_relations = $window_relations ? unserialize($window_relations) : Array ();
			if (array_key_exists($wid, $window_relations)) {
				unset($window_relations[$wid]);
				$this->Application->StoreVar('window_relations', serialize($window_relations));
			}*/
		}

		return $ret;
	}

	/**
	 * Returns variables with values that should be passed through with this link + variable list
	 *
	 * @param Array $params
	 * @return Array
	 * @access public
	 */
	public function getPassThroughVariables(&$params)
	{
		static $cached_pass_through = null;

		if ( isset($params['no_pass_through']) && $params['no_pass_through'] ) {
			unset($params['no_pass_through']);

			return Array ();
		}

		// because pass through is not changed during script run, then we can cache it
		if ( is_null($cached_pass_through) ) {
			$cached_pass_through = Array ();
			$pass_through = $this->Application->GetVar('pass_through');

			if ( $pass_through ) {
				// names of variables to pass to each link
				$cached_pass_through['pass_through'] = $pass_through;
				$pass_through = explode(',', $pass_through);

				foreach ($pass_through as $pass_through_var) {
					$cached_pass_through[$pass_through_var] = $this->Application->GetVar($pass_through_var);
				}
			}
		}

		return $cached_pass_through;
	}

	/**
	 * Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all
	 *
	 * @param string $prefix
	 * @param string $index_file
	 * @param Array $params
	 * @return string
	 * @access protected
	 */
	protected function getIndexFile($prefix, $index_file = null, &$params)
	{
		static $cache = Array ();

		if ( isset($params['index_file']) ) {
			$index_file = $params['index_file'];
			unset($params['index_file']);

			return $index_file;
		}

		if ( isset($index_file) ) {
			return $index_file;
		}

		if ( defined('INDEX_FILE') ) {
			return INDEX_FILE;
		}

		// detect index file only once for given script and $cut_prefix
		$php_self = $_SERVER['PHP_SELF'];
		$cut_prefix = BASE_PATH . '/' . trim($prefix, '/');

		if ( isset($cache[$php_self . ':' . $cut_prefix]) ) {
			return $cache[$php_self . ':' . $cut_prefix];
		}

		$cache[$php_self . ':' . $cut_prefix] = trim(preg_replace('/' . preg_quote($cut_prefix, '/') . '(.*)/', '\\1', $php_self), '/');

		return $cache[$php_self . ':' . $cut_prefix];
	}

	/**
	 * Returns theme template filename and it's corresponding page_id based on given seo template
	 *
	 * @param string $seo_template
	 * @return string
	 * @access public
	 */
	public function getPhysicalTemplate($seo_template)
	{
		$physical_template = array_search($seo_template, $this->structureTemplateMapping);

		if ( $physical_template === false || substr($physical_template, 0, 3) == 'id:' ) {
			// physical template from ".smsignore" file OR virtual template
			return $seo_template;
		}

		list ($physical_template,) = explode(':', $physical_template, 2); // template_path:theme_id => seo_template

		return $physical_template;
	}

	/**
	 * Returns template name, that corresponds with given virtual (not physical) page id
	 *
	 * @param int $page_id
	 * @return string|bool
	 * @access public
	 */
	public function getVirtualPageTemplate($page_id)
	{
		return isset($this->structureTemplateMapping['id:' . $page_id]) ? $this->structureTemplateMapping['id:' . $page_id] : false;
	}

	/**
	 * Loads template mapping for Front-End
	 *
	 * @return void
	 * @access public
	 */
	public function LoadStructureTemplateMapping()
	{
		if (!$this->Application->isAdmin) {
			$category_helper = $this->Application->recallObject('CategoryHelper');
			/* @var $category_helper CategoryHelper */

			$this->structureTemplateMapping = $category_helper->getTemplateMapping();
		}
	}

	/**
	 * Removes tpl part from template name + resolved template ID to name
	 *
	 * @param string $default_template
	 * @return string
	 * @access public
	 */
	public function getTemplateName($default_template = '')
	{
		if ( $this->Application->GetVarDirect('t', 'Get') !== false ) {
			// template name is passed directly in url (GET method)
			$t = $this->Application->GetVarDirect('t', 'Get');
		}
		elseif ( $this->Application->GetVar('env') && $this->Application->RewriteURLs() && $this->Application->GetVar('t') ) {
			// if t was set through env, even in mod_rewrite mode!
			$t = $this->Application->GetVar('t');
		}
		else {
			$t = trim($default_template ? $default_template : 'index', '/');
		}

		return trim(preg_replace('/\.tpl$/', '', $t), '/');
	}

	/**
	 * Prepares case, when requested page wasn't found
	 *
	 * @param int $theme_id
	 * @return array
	 */
	public function prepare404($theme_id = null)
	{
		if ( !isset($theme_id) ) {
			$theme_id = $this->Application->GetVar('m_theme');
		}

		$not_found = $this->Application->ConfigValue('ErrorTemplate');
		$vars['t'] = $not_found ? $not_found : 'error_notfound';

		$themes_helper = $this->Application->recallObject('ThemesHelper');
		/* @var $themes_helper kThemesHelper */

		$vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $theme_id);

		header('HTTP/1.0 404 Not Found');

		return $vars;
	}
}