Index: branches/5.0.x/core/units/helpers/mod_rewrite_helper.php
===================================================================
--- branches/5.0.x/core/units/helpers/mod_rewrite_helper.php	(revision 12783)
+++ branches/5.0.x/core/units/helpers/mod_rewrite_helper.php	(revision 12784)
@@ -1,960 +1,951 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 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 kModRewriteHelper extends kHelper {
 
 		/**
 		 * Holds a refererence to httpquery
 		 *
 		 * @var kHttpQuery
 		 */
 		var $HTTPQuery = null;
 
 		/**
 		 * Parts found during url parsing
 		 *
 		 * @var Array
 		 */
 		var $_partsFound = Array ();
 
 		/**
 		 * Category item prefix, that was found
 		 *
 		 * @var string
 		 */
 		var $_modulePrefix = false;
 
 		/**
 		 * Constructor of kModRewriteHelper class
 		 *
 		 * @return kModRewriteHelper
 		 */
 		function kModRewriteHelper()
 		{
 			parent::kHelper();
 
 			$this->HTTPQuery =& $this->Application->recallObject('HTTPQuery');
 		}
 
 		function processRewriteURL()
 		{
 			$passed = Array ();
 			$url = $this->HTTPQuery->Get('_mod_rw_url_');
 			if (substr($url, -5) == '.html') {
 				$url = substr($url, 0, strlen($url) - 5);
 			}
 
 			$restored = false;
 			$sql = 'SELECT Data, Cached
 					FROM ' . TABLE_PREFIX . 'Cache
 					WHERE VarName = "mod_rw_' . md5($url) . '"';
 			$cache = $this->Conn->GetRow($sql);
 
 			if (false && $cache && $cache['Cached'] > 0) {
 				// not used for now
 				$cache = unserialize($cache['Data']);
 				$vars = $cache['vars'];
 				$passed = $cache['passed'];
 				$restored = true;
 			}
 			else {
 				$vars = $this->parseRewriteURL($url);
 				$passed = $vars['pass']; // also used in bottom of this method
 				unset($vars['pass']);
 
 				$cache = Array ('vars' => $vars, 'passed' => $passed);
 
 				$fields_hash = Array (
 					'VarName' => 'mod_rw_' . md5($url),
 					'Data' => serialize($cache),
 					'Cached' => adodb_mktime(),
 				);
 				$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Cache', 'REPLACE');
 
 				if (array_key_exists('t', $this->HTTPQuery->Post) && $this->HTTPQuery->Post['t']) {
 					// template from POST overrides template from URL.
 					$vars['t'] = $this->HTTPQuery->Post['t'];
 					if (isset($vars['is_virtual']) && $vars['is_virtual']) {
 						$vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS)
 					}
 				}
 
 				unset($vars['is_virtual']);
 			}
 
 			foreach ($vars as $name => $value) {
 				$this->HTTPQuery->Set($name,$value);
 			}
 
 //			if ($restored) {
 				$this->InitAll();
 //			}
 
 			$this->HTTPQuery->finalizeParsing($passed);
 		}
 
 		function parseRewriteURL($url)
 		{
 			$sql = 'SELECT Data, Cached
 					FROM ' . TABLE_PREFIX . 'Cache
 					WHERE VarName = "mod_rw_' . md5($url) . '"';
 			$vars = $this->Conn->GetRow($sql);
 
 			if (false && $vars && $vars['Cached'] > 0) {
 				// not used for now
 				$vars = unserialize($menu['Data']);
 				return $vars;
 			}
 
 			$vars = Array ('pass' => Array ('m'));
 			$url_parts = $url ? explode('/', trim(mb_strtolower($url), '/')) : Array ();
 
 			if (($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts) {
 				$this->_setDefaultValues($vars);
 			}
 
 			if (!$url_parts) {
 				$this->InitAll();
 				$vars['t'] = $this->HTTPQuery->getDefaultTemplate('');
 
 				return $vars;
 			}
 			else {
 				$vars['t'] = '';
 			}
 
 			$this->_parseLanguage($url_parts, $vars);
 			$this->_parseTheme($url_parts, $vars);
-			$this->_setDefaultPages($vars);
-
-			if ($this->_parsePage($url_parts, $vars)) {
-				$this->_partsFound[] = 'parsePage';
-			}
 
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_page>
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_page> (category-based section template)
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_item>
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_item> (category-based detail template)
 			// http://site-url/<language>/<theme>/<rl_injections>/<category>[_<category_page>]/<rl_part> (customized url)
 
 			if ( $this->processRewriteListeners($url_parts, $vars) ) {
 				return $vars;
 			}
 
 			if ($this->_parsePhisycalTemplate($url_parts, $vars)) {
 				$this->_partsFound[] = 'parsePhisycalTemplate';
 			}
 
 			if (($this->_modulePrefix === false) && in_array('parseCategory', $this->_partsFound)) {
 				// no item found, but category found -> module index page
 				foreach ($this->Application->RewriteListeners as $prefix => $listener) {
 					// no idea what module we are talking about, so pass info form all modules
 					$vars['pass'][] = $prefix;
 				}
 
 				return $vars;
 			}
 
 			if (!$this->_partsFound) {
 				$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'], $vars['m_theme']);
 
 				header('HTTP/1.0 404 Not Found');
 			}
 
 			return $vars;
 		}
 
 		function InitAll()
 		{
 			$this->Application->VerifyLanguageId();
 			$this->Application->Phrases->Init('phrases');
 			$this->Application->VerifyThemeId();
 		}
 
 		/**
 		 * Processes url using rewrite listeners
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function processRewriteListeners(&$url_parts, &$vars)
 		{
 			$this->initRewriteListeners();
 
+			$page_number = $this->_parsePage($url_parts, $vars);
+
+			if ($page_number) {
+				$this->_partsFound[] = 'parsePage';
+			}
+
 			foreach ($this->Application->RewriteListeners as $prefix => $listener) {
+				// set default page
+				$vars[$prefix . '_Page'] = 1; // will override page in session in case, when none is given in url
+
+				if ($page_number) {
+					// page given in url - use it
+					$vars[$prefix . '_id'] = 0;
+					$vars[$prefix . '_Page'] = $page_number;
+				}
+
 				$listener_result = $listener[0]->$listener[1](REWRITE_MODE_PARSE, $prefix, $vars, $url_parts);
 				if ($listener_result === false) {
 					// will not proceed to other methods
 					return true;
 				}
 			}
 
 			// will proceed to other methods
 			return false;
 		}
 
 		/**
 		 * Parses real template name from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parsePhisycalTemplate($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			do {
 				$template_path = implode('/', $url_parts);
 
 				$t_parts['path'] = dirname($template_path) == '.' ? '' : '/' . dirname($template_path);
 				$t_parts['file'] = basename($template_path);
 
 				$sql = 'SELECT FileId
 						FROM ' . TABLE_PREFIX . 'ThemeFiles
 						WHERE (ThemeId = ' . $vars['m_theme'] . ') AND (FilePath = ' . $this->Conn->qstr($t_parts['path']) . ') AND (FileName = ' . $this->Conn->qstr($t_parts['file'] . '.tpl') . ')';
 				$template_found = $this->Conn->GetOne($sql);
 
 				if (!$template_found) {
 					array_shift($url_parts);
 				}
 			} while (!$template_found && $url_parts);
 
 			if ($template_found) {
 				$vars['t'] = $template_path;
 
 				$themes_helper =& $this->Application->recallObject('ThemesHelper');
 				/* @var $themes_helper kThemesHelper */
 
 				$vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']);
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Parses category part of url, build main part of url
 		 *
 		 * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE.
 		 * @param string $prefix Prefix, that listener uses for system integration
 		 * @param Array $params Params, that are used for url building or created during url parsing.
 		 * @param Array $url_parts Url parts to parse (only for parsing).
 		 * @param bool $keep_events Keep event names in resulting url (only for building).
 		 * @return bool|string|Array Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener.
 		 */
 		function MainRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false)
 		{
 			if ($rewrite_mode == REWRITE_MODE_BUILD) {
 				return $this->_buildMainUrl($prefix, $params, $keep_events);
 			}
 
 			if ( $this->_parseFriendlyUrl($url_parts, $params) ) {
 				// friendly urls work like exact match only!
 				return false;
 			}
 
 			if ($this->_parseCategory($url_parts, $params)) {
 				$this->_partsFound[] = 'parseCategory';
 			}
 
 			return true;
 		}
 
 		/**
 		 * Build main part of every url
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return string
 		 */
 		function _buildMainUrl($prefix_special, &$params, $keep_events)
 		{
 			$ret = '';
 			list ($prefix) = explode('.', $prefix_special);
 
 			$processed_params = $this->getProcessedParams($prefix_special, $params, $keep_events);
 			if ($processed_params === false) {
 				return '';
 			}
 
 			// add language
 			$default_language_id = $this->Application->GetDefaultLanguageId();
 			if ($processed_params['m_lang'] && ($processed_params['m_lang'] != $default_language_id)) {
 				$language_name = $this->Application->getCache('language_names', $processed_params['m_lang']);
 				if ($language_name === false) {
 					$sql = 'SELECT PackName
 							FROM ' . TABLE_PREFIX . 'Language
 							WHERE LanguageId = ' . $processed_params['m_lang'];
 					$language_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('language_names', $processed_params['m_lang'], $language_name);
 				}
 
 				$ret .= $language_name . '/';
 			}
 
 			// add theme
 			$default_theme_id = $this->Application->GetDefaultThemeId();
 			if ($processed_params['m_theme'] && ($processed_params['m_theme'] != $default_theme_id)) {
 				$theme_name = $this->Application->getCache('theme_names', $processed_params['m_theme']);
 				if ($theme_name === false) {
 					$sql = 'SELECT Name
 							FROM ' . TABLE_PREFIX . 'Theme
 							WHERE ThemeId = ' . $processed_params['m_theme'];
 					$theme_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('theme_names', $processed_params['m_theme'], $theme_name);
 
 				}
 
 				$ret .= $theme_name . '/';
 			}
 
 			// inject custom url parts made by other rewrite listeners just after language/theme url parts
 			if ($params['inject_parts']) {
 				$ret .= implode('/', $params['inject_parts']) . '/';
 			}
 
 			// add category
 			if ($processed_params['m_cat_id'] > 0 && $params['pass_category']) {
 				$category_filename = $this->Application->getFilename('c', $processed_params['m_cat_id']);
 
 				preg_match('/^Content\/(.*)/i', $category_filename, $regs);
 
 				if ($regs) {
 					$template = array_key_exists('t', $params) ? $params['t'] : false;
 
 					if (strtolower($regs[1]) == strtolower($template)) {
 						// we could have category path like "Content/<template_path>" in this case remove template
 						$params['pass_template'] = false;
 					}
 
 					$ret .= $regs[1] . '/';
 				}
 
 				$params['category_processed'] = true;
 			}
 
 			// reset category page
 			$force_page_adding = false;
 			if (array_key_exists('reset', $params) && $params['reset']) {
 				unset($params['reset']);
 
 				if ($processed_params['m_cat_id']) {
 					$processed_params['m_cat_page'] = 1;
 					$force_page_adding = true;
 				}
 			}
 
 			if ((array_key_exists('category_processed', $params) && $params['category_processed'] && ($processed_params['m_cat_page'] > 1)) || $force_page_adding) {
 				// category name was added before AND category page number found
 				$ret = rtrim($ret, '/') . '_' . $processed_params['m_cat_page'] . '/';
 			}
 
 			$template = array_key_exists('t', $params) ? $params['t'] : false;
 			$category_template = ($processed_params['m_cat_id'] > 0) && $params['pass_category'] ? $this->Application->getCache('category_designs', $processed_params['m_cat_id']) : '';
 
 			if ((strtolower($template) == '__default__') && ($processed_params['m_cat_id'] == 0)) {
 				// for "Home" category set template to index when not set
 				$template = 'index';
 			}
 
 			// remove template from url if it is category index cached template
 			if (($template == $category_template) || (mb_strtolower($template) == '__default__')) {
 				// given template is also default template for this category or '__default__' given
 				$params['pass_template'] = false;
 			}
 
 			if ($template && $params['pass_template']) {
 				$ret .= $template . '/';
 			}
 
 			return mb_strtolower( rtrim($ret, '/') );
 		}
 
 		/**
 		 * Gets language part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parseLanguage(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$url_part = reset($url_parts);
 
 			$sql = 'SELECT LanguageId, IF(LOWER(PackName) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryLang) AS SortKey
 					FROM ' . TABLE_PREFIX . 'Language
 					WHERE Enabled = 1
 					ORDER BY SortKey DESC';
 			$language_info = $this->Conn->GetRow($sql);
 			$this->Application->Phrases = new PhrasesCache();
 
 			if ($language_info && $language_info['LanguageId'] && $language_info['SortKey']) {
 				// primary language will be selected in case, when $url_part doesn't match to other's language pack name
 				// don't use next enabled language, when primary language is disabled
 				$vars['m_lang'] = $language_info['LanguageId'];
 
 				if ($language_info['SortKey'] == 2) {
 					// language was found by pack name
 					array_shift($url_parts);
 				}
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Gets theme part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parseTheme(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$url_part = reset($url_parts);
 
 			$sql = 'SELECT ThemeId, IF(LOWER(Name) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryTheme) AS SortKey
 					FROM ' . TABLE_PREFIX . 'Theme
 					WHERE Enabled = 1
 					ORDER BY SortKey DESC';
 			$theme_info = $this->Conn->GetRow($sql);
 
 			if ($theme_info && $theme_info['ThemeId'] && $theme_info['SortKey']) {
 				// primary theme will be selected in case, when $url_part doesn't match to other's theme name
 				// don't use next enabled theme, when primary theme is disabled
 				$vars['m_theme'] = $theme_info['ThemeId'];
 
 				if ($theme_info['SortKey'] == 2) {
 					// theme was found by name
 					array_shift($url_parts);
 				}
 
 				return true;
 			}
 
 			$vars['m_theme'] = 0; // required, because used later for category/template detection
 
 			return false;
 		}
 
 		/**
 		 * Checks if whole url_parts matches a whole In-CMS page
 		 *
 		 * @param array $url_parts
 		 * @return boolean
 		 */
 		function _parseFriendlyUrl($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$sql = 'SELECT CategoryId, NamedParentPath
 					FROM ' . TABLE_PREFIX . 'Category
 					WHERE FriendlyURL = ' . $this->Conn->qstr(implode('/', $url_parts));
 
 			$friendly = $this->Conn->GetRow($sql);
 			if ($friendly) {
 				$vars['m_cat_id'] = $friendly['CategoryId'];
 				$vars['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']);
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
-		 * Set's 1st page for all modules (will be used when not passed in url)
-		 *
-		 * @param Array $vars
-		 */
-		function _setDefaultPages(&$vars)
-		{
-			// set 1st page for all rewrite listeners, since we don't know which of them will need it
-			foreach ($this->Application->RewriteListeners as $prefix => $listener) {
-				$vars[$prefix . '_Page'] = 1;
-			}
-		}
-
-		/**
 		 * Set's page (when found) to all modules
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return string
 		 *
 		 * @todo Should find a way, how to determine what rewrite listerner page is it
 		 */
 		function _parsePage(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$page_number = end($url_parts);
 			if (!is_numeric($page_number)) {
 				return false;
 			}
 
-			// set module pages for all modules, since we don't know which module will need it
-			foreach ($this->Application->RewriteListeners as $prefix => $listener) {
-				$vars[$prefix . '_id'] = 0;
-				$vars[$prefix . '_Page'] = $page_number;
-			}
-
 			array_pop($url_parts);
 
-			return true;
+			return $page_number;
 		}
 
 		/**
 		 * Remove page numbers for all rewrite listeners
 		 *
 		 * @todo Should find a way, how to determine what rewrite listerner page is it
 		 */
 		function removePages()
 		{
 			foreach ($this->Application->RewriteListeners as $prefix => $listener) {
 				$this->Application->DeleteVar($prefix . '_Page');
 			}
 		}
 
 		/**
 		 * Extracts category part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parseCategory($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$res = false;
 			$url_part = array_shift($url_parts);
 
 			$category_id = 0;
 			$last_category_info = false;
 			$category_path = $url_part == 'content' ? '' : 'content';
 
 			do {
 				$category_path = trim($category_path . '/' . $url_part, '/');
 				// bb_<topic_id> -> forums/bb_2
 				if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $rets) ) {
 					$category_path = $rets[1];
 					$vars['m_cat_page'] = $rets[2];
 				}
 
 				$sql = 'SELECT CategoryId, IsIndex, NamedParentPath
 						FROM ' . TABLE_PREFIX . 'Category
 						WHERE Status IN (1,4) AND (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)';
 				$category_info = $this->Conn->GetRow($sql);
 
 				if ($category_info !== false) {
 					$last_category_info = $category_info;
 					$url_part = array_shift($url_parts);
 					$res = true;
 				}
 			} while ($category_info !== false && $url_part);
 
 			if ($last_category_info) {
 				// IsIndex = 2 is a Container-only page, meaning it should go to index-page child
 				if ($last_category_info['IsIndex'] == 2) {
 					$sql = 'SELECT CategoryId, NamedParentPath
 							FROM ' . TABLE_PREFIX . 'Category
 							WHERE (ParentId = ' . $last_category_info['CategoryId'] . ') AND (IsIndex = 1) AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)';
 					$category_info = $this->Conn->GetRow($sql);
 
 					if ($category_info) {
 						// when index sub-page is found use it, otherwise use container page
 						$last_category_info = $category_info;
 					}
 				}
 
 				// 1. Set virtual page as template, this will be replaced to physical template later in kApplication::Run.
 				// 2. Don't set CachedTemplate field as template here, because we will loose original page associated with it's cms blocks!
 				$vars['t'] = strtolower( preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath']) );
 
 				$vars['m_cat_id'] = $last_category_info['CategoryId'];
 				$vars['is_virtual'] = true; // for template from POST, strange code there!
 			}
 			else {
 				$vars['m_cat_id'] = 0;
 			}
 
 			return $res;
 		}
 
 		/**
 		 * Builds/parses category item part of url
 		 *
 		 * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE.
 		 * @param string $prefix Prefix, that listener uses for system integration
 		 * @param Array $params Params, that are used for url building or created during url parsing.
 		 * @param Array $url_parts Url parts to parse (only for parsing).
 		 * @param bool $keep_events Keep event names in resulting url (only for building).
 		 * @return bool Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener.
 		 */
 		function CategoryItemRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false)
 		{
 			static $parsed = false;
 
 			if ($rewrite_mode == REWRITE_MODE_BUILD) {
 				return $this->_buildCategoryItemUrl($prefix, $params, $keep_events);
 			}
 
 			if (!$parsed) {
 				$this->_modulePrefix = $this->_parseCategoryItemUrl($url_parts, $params);
 				if ($this->_modulePrefix !== false) {
 					$params['pass'][] = $this->_modulePrefix;
 					$this->_partsFound[] = 'parseCategoryItemUrl';
 				}
 
 				$parsed = true;
 			}
 
 			return true;
 		}
 
 		/**
 		 * Build category teim part of url
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return string
 		 */
 		function _buildCategoryItemUrl($prefix_special, &$params, $keep_events)
 		{
 			$ret = '';
 			list ($prefix) = explode('.', $prefix_special);
 			$processed_params = $this->getProcessedParams($prefix_special, $params, $keep_events);
 
 			if ($processed_params === false) {
 				return '';
 			}
 
 			if ($processed_params[$prefix_special . '_id']) {
 				// this allows to fill 3 cache records with one query (see this method for details)
 				$category_id = array_key_exists('m_cat_id', $params) ? $params['m_cat_id'] : $this->Application->GetVar('m_cat_id');
 				$category_filename = $this->Application->getFilename('c', $category_id);
 
 				// if template is also item template of category, then remove template
 				$template = array_key_exists('t', $params) ? $params['t'] : false;
 
 				$item_template = $this->GetItemTemplate($category_id, $prefix);
 
 				if ($template == $item_template || strtolower($template) == '__default__') {
 					// given template is also default template for this category item or '__default__' given
 					$params['pass_template'] = false;
 				}
 
 				// get item's filename
 				if ($prefix == 'bb') {
 					$ret .= 'bb_' . $processed_params[$prefix_special . '_id'] . '/';
 				}
 				else {
 					$filename = $this->Application->getFilename($prefix, $processed_params[$prefix_special . '_id'], $category_id);
 					if ($filename !== false) {
 						$ret .= $filename . '/';
 					}
 				}
 			} else {
 				if ($processed_params[$prefix_special . '_Page'] == 1) {
 					// when printing category items and we are on the 1st page -> there is no information about
 					// category item prefix and $params['pass_category'] will not be added automatically
 					$params['pass_category'] = true;
 				}
 				else {
 					$ret .= $processed_params[$prefix_special . '_Page'] . '/';
 				}
 			}
 
 			return mb_strtolower( rtrim($ret, '/') );
 		}
 
 		/**
 		 * Sets template and id, corresponding to category item given in url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool|string
 		 */
 		function _parseCategoryItemUrl(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$item_filename = end($url_parts);
 			if (is_numeric($item_filename)) {
 				// this page, don't process here
 				return false;
 			}
 
 			if (preg_match('/^bb_([\d]+)/', $item_filename, $regs)) {
 				// process topics separatly, because they don't use item filenames
 				array_pop($url_parts);
 
 				return $this->_parseTopicUrl($regs[1], $vars);
 			}
 
 			// locating the item in CategoryItems by filename to detect its ItemPrefix and its category ParentPath
 			$sql = 'SELECT ci.ItemResourceId, ci.ItemPrefix, c.ParentPath, ci.CategoryId
 					FROM ' . TABLE_PREFIX . 'CategoryItems AS ci
 					LEFT JOIN ' . TABLE_PREFIX . 'Category AS c ON c.CategoryId = ci.CategoryId
 					WHERE (ci.CategoryId = ' . (int)$vars['m_cat_id'] . ') AND (ci.Filename = ' . $this->Conn->qstr($item_filename) . ')';
 			$cat_item = $this->Conn->GetRow($sql);
 
 			if ($cat_item !== false) {
 				// item found
 				$module_prefix = $cat_item['ItemPrefix'];
 				$item_template = $this->GetItemTemplate($cat_item, $module_prefix);
 
 				// converting ResourceId to correpsonding Item id
 				$module_config = $this->Application->getUnitOptions($module_prefix);
 
 				$sql = 'SELECT ' . $module_config['IDField'] . '
 						FROM ' . $module_config['TableName'] . '
 					 	WHERE ResourceId = ' . $cat_item['ItemResourceId'];
 				$item_id = $this->Conn->GetOne($sql);
 
 				array_pop($url_parts);
 
 				if ($item_id) {
 					if ($item_template) {
 						// when template is found in category -> set it
 						$vars['t'] = $item_template;
 					}
 
 					// we have category item id
 					$vars[$module_prefix . '_id'] = $item_id;
 
 					return $module_prefix;
 				}
 			}
 
 			return false;
 		}
 
 		/**
 		 * Set's template and topic id corresponding to topic given in url
 		 *
 		 * @param int $topic_id
 		 * @param Array $vars
 		 * @return string
 		 */
 		function _parseTopicUrl($topic_id, &$vars)
 		{
 			$sql = 'SELECT c.ParentPath, c.CategoryId
 					FROM ' . TABLE_PREFIX . 'Category AS c
 					WHERE c.CategoryId = ' . (int)$vars['m_cat_id'];
 			$cat_item = $this->Conn->GetRow($sql);
 
 			$item_template = $this->GetItemTemplate($cat_item, 'bb');
 
 			if ($item_template) {
 				$vars['t'] = $item_template;
 			}
 
 			$vars['bb_id'] = $topic_id;
 
 			return 'bb';
 		}
 
 		/**
 		 * Returns enviroment variable values for given prefix (uses directly given params, when available)
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return Array
 		 */
 		function getProcessedParams($prefix_special, &$params, $keep_events)
 		{
 			list ($prefix) = explode('.', $prefix_special);
 
 			$query_vars = $this->Application->getUnitOption($prefix, 'QueryString');
 			if (!$query_vars) {
 				// given prefix doesn't use "env" variable to pass it's data
 				return false;
 			}
 
 			$event_key = array_search('event', $query_vars);
 			if ($event_key) {
 				// pass through event of this prefix
 				unset($query_vars[$event_key]);
 			}
 
 			if (array_key_exists($prefix_special . '_event', $params) && !$params[$prefix_special . '_event']) {
 				// if empty event, then remove it from url
 				unset( $params[$prefix_special . '_event'] );
 			}
 
 			// if pass events is off and event is not implicity passed
 			if (!$keep_events && !array_key_exists($prefix_special . '_event', $params)) {
 				unset($params[$prefix_special . '_event']); // remove event from url if requested
 				//otherwise it will use value from get_var
 			}
 
 			$processed_params = Array ();
 			foreach ($query_vars as $index => $var_name) {
 				// if value passed in params use it, otherwise use current from application
 				$var_name = $prefix_special . '_' . $var_name;
 				$processed_params[$var_name] = array_key_exists($var_name, $params) ? $params[$var_name] : $this->Application->GetVar($var_name);
 				if (array_key_exists($var_name, $params)) {
 					unset($params[$var_name]);
 				}
 			}
 
 			return $processed_params;
 		}
 
 		/**
 		 * Returns module item details template specified in given category custom field for given module prefix
 		 *
 		 * @param int|Array $category
 		 * @param string $module_prefix
 		 * @return string
 		 */
 		function GetItemTemplate($category, $module_prefix)
 		{
 			$cache_key = serialize($category) . '_' . $module_prefix;
 
 			$cached_value = $this->Application->getCache(__CLASS__ . __FUNCTION__, $cache_key);
 			if ($cached_value !== false) {
 				return $cached_value;
 			}
 
 			if (!is_array($category)) {
 				if ($category == 0) {
 					$category = $this->Application->findModule('Var', $module_prefix, 'RootCat');
 				}
 				$sql = 'SELECT c.ParentPath, c.CategoryId
 						FROM ' . TABLE_PREFIX . 'Category AS c
 						WHERE c.CategoryId = ' . $category;
 				$category = $this->Conn->GetRow($sql);
 			}
 			$parent_path = implode(',',explode('|', substr($category['ParentPath'], 1, -1)));
 
 			// item template is stored in module' system custom field - need to get that field Id
 			$item_template_field_id = $this->_getItemTemplateCustomField($module_prefix);
 
 			// looking for item template through cats hierarchy sorted by parent path
 			$query = '	SELECT ccd.l1_cust_' . $item_template_field_id . ',
 									FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') AS Ord1,
 									c.CategoryId, c.Name, ccd.l1_cust_' . $item_template_field_id . '
 						FROM ' . TABLE_PREFIX . 'Category AS c
 						LEFT JOIN ' . TABLE_PREFIX . 'CategoryCustomData AS ccd
 						ON ccd.ResourceId = c.ResourceId
 						WHERE c.CategoryId IN (' . $parent_path . ') AND ccd.l1_cust_' . $item_template_field_id . ' != \'\'
 						ORDER BY FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') DESC';
 			$item_template = $this->Conn->GetOne($query);
 
 			$this->Application->setCache(__CLASS__ . __FUNCTION__, $cache_key, $item_template);
 
 			return $item_template;
 		}
 
 		/**
 		 * Loads all registered rewrite listeners, so they could be quickly accessed later
 		 *
 		 */
 		function initRewriteListeners()
 		{
 			static $init_done = false;
 
 			if ($init_done) {
 				return ;
 			}
 
 			foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
 				list ($listener_prefix, $listener_method) = explode(':', $listener_data['listener']);
 				$listener =& $this->Application->recallObject($listener_prefix);
 
 				$this->Application->RewriteListeners[$prefix] = Array (&$listener, $listener_method);
 			}
 
 			$init_done = true;
 		}
 
 		/**
 		 * Returns category custom field id, where given module prefix item template name is stored
 		 *
 		 * @param string $module_prefix
 		 * @return int
 		 */
 		function _getItemTemplateCustomField($module_prefix)
 		{
 			$cached_value = $this->Application->getCache(__CLASS__ . __FUNCTION__, $module_prefix);
 			if ($cached_value !== false) {
 				return $cached_value;
 			}
 
 			$sql = 'SELECT CustomFieldId
 				 	FROM ' . TABLE_PREFIX . 'CustomField
 				 	WHERE FieldName = ' . $this->Conn->qstr($module_prefix . '_ItemTemplate');
 			$item_template_field_id = $this->Conn->GetOne($sql);
 
 			$this->Application->setCache(__CLASS__ . __FUNCTION__, $module_prefix, $item_template_field_id);
 
 			return $item_template_field_id;
 		}
 
 		/**
 		 * Sets default parsed values before actual url parsing
 		 *
 		 * @param Array $vars
 		 */
 		function _setDefaultValues(&$vars)
 		{
 			$defaults = Array ('m_cat_id' => 0, 'm_cat_page' => 1, 'm_opener' => 's', 't' => 'index');
 
 			foreach ($defaults as $default_key => $default_value) {
 				// bug: null is never returned
 				if ($this->HTTPQuery->Get($default_key) == null) {
 					$vars[$default_key] = $default_value;
 				}
 			}
 		}
 	}
 ?>
\ No newline at end of file