Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php	(revision 14786)
+++ branches/5.2.x/core/kernel/application.php	(revision 14787)
@@ -1,2766 +1,2783 @@
 <?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!');
 
 /**
 * Basic class for Kernel4-based Application
 *
 * This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
 * The class incapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
 * <br>
 * The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
 * This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method,
 * which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br>
 * See singleton pattern by GOF.
 */
 class kApplication implements kiCacheable {
 
 	/**
 	 * Location of module helper class (used in installator too)
 	 */
 	const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php';
 
 	/**
 	 * Is true, when Init method was called already, prevents double initialization
 	 *
 	 * @var bool
 	 */
 	var $InitDone = false;
 
 	/**
 	* Holds internal NParser object
 	* @access private
 	* @var NParser
 	*/
 	var $Parser;
 
 	/**
 	* Holds parser output buffer
 	* @access private
 	* @var string
 	*/
 	var $HTML;
 
 	/**
 	* The main Factory used to create
 	* almost any class of kernel and
 	* modules
 	*
 	* @access private
 	* @var kFactory
 	*/
 	var $Factory;
 
 	/**
 	 * Template names, that will be used instead of regular templates
 	 *
 	 * @var Array
 	 */
 	var $ReplacementTemplates = Array ();
 
 	/**
 	 * Mod-Rewrite listeners used during url building and parsing
 	 *
 	 * @var Array
 	 */
 	var $RewriteListeners = Array ();
 
 	/**
 	 * Reference to debugger
 	 *
 	 * @var Debugger
 	 */
 	var $Debugger = null;
 
 	/**
 	 * Holds all phrases used
 	 * in code and template
 	 *
 	 * @var PhrasesCache
 	 */
 	var $Phrases;
 
 	/**
 	 * Modules table content, key - module name
 	 *
 	 * @var Array
 	 */
 	var $ModuleInfo = Array();
 
 	/**
 	 * Holds DBConnection
 	 *
 	 * @var kDBConnection
 	 */
 	var $Conn = null;
 
 	/**
 	 * Maintains list of user-defined error handlers
 	 *
 	 * @var Array
 	 */
 	var $errorHandlers = Array();
 
 	/**
 	 * Maintains list of user-defined exception handlers
 	 *
 	 * @var Array
 	 */
 	var $exceptionHandlers = Array();
 
 	// performance needs:
 	/**
 	 * Holds a reference to httpquery
 	 *
 	 * @var kHttpQuery
 	 */
 	var $HttpQuery = null;
 
 	/**
 	 * Holds a reference to UnitConfigReader
 	 *
 	 * @var kUnitConfigReader
 	 */
 	var $UnitConfigReader = null;
 
 	/**
 	 * Holds a reference to Session
 	 *
 	 * @var Session
 	 */
 	var $Session = null;
 
 	/**
 	 * Holds a ref to kEventManager
 	 *
 	 * @var kEventManager
 	 */
 	var $EventManager = null;
 
 	/**
 	 * Holds a ref to kUrlManager
 	 *
 	 * @var kUrlManager
 	 * @access public
 	 */
 	public $UrlManager = null;
 
 	/**
 	 * Ref for TemplatesCache
 	 *
 	 * @var TemplatesCache
 	 */
 	var $TemplatesCache = null;
 
 	var $CompilationCache = array(); //used when compiling templates
 	var $CachedProcessors = array(); //used when running compiled templates
 
 	var $LambdaElements = 1; // for autonumbering unnamed RenderElements [any better place for this prop? KT]
 
 	/**
 	 * Holds current NParser tag while parsing, can be used in error messages to display template file and line
 	 *
 	 * @var _BlockTag
 	 */
 	var $CurrentNTag = null;
 
 	/**
 	 * Object of unit caching class
 	 *
 	 * @var kCacheManager
 	 */
 	var $cacheManager = null;
 
 	/**
 	 * Tells, that administrator has authenticated in administrative console
 	 * Should be used to manipulate data change OR data restrictions!
 	 *
 	 * @var bool
 	 */
 	var $isAdminUser = false;
 
 	/**
 	 * Tells, that admin version of "index.php" was used, nothing more!
 	 * Should be used to manipulate data display!
 	 *
 	 * @var bool
 	 */
 	var $isAdmin = false;
 
  	/**
 	 * Instance of site domain object
 	 *
 	 * @var kDBItem
 	 */
 	var $siteDomain = null;
 
 	/**
 	 * Prevent kApplication class to be created directly, only via Instance method
 	 *
 	 */
 	protected function __construct()
 	{
 
 	}
 
 	/**
 	* Returns kApplication instance anywhere in the script.
  	*
  	* This method should be used to get single kApplication object instance anywhere in the
  	* Kernel-based application. The method is guaranteed to return the SAME instance of kApplication.
  	* Anywhere in the script you could write:
  	* <code>
  	*		$application =& kApplication::Instance();
  	* </code>
  	* or in an object:
  	* <code>
  	*		$this->Application =& kApplication::Instance();
  	* </code>
  	* to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
  	* To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
  	* BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
  	* create and return default KernelApplication instance.
  	*
  	* Pattern: Singleton
  	*
  	* @static
  	* @access public
 	* @return kApplication
 	*/
 	public static function &Instance()
 	{
 		static $instance = false;
 
 		if (!$instance) {
 			$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
 			$instance = new $class();
 			$instance->Application =& $instance;
 		}
 
 		return $instance;
 	}
 
 	/**
 	* Initializes the Application
  	*
  	* @access public
 	* @see kHTTPQuery
 	* @see Session
 	* @see TemplatesCache
 	* @return bool Was Init actually made now or before
 	*/
 	public function Init()
 	{
 		if ( $this->InitDone ) {
 			return false;
 		}
 
 		$this->isAdmin = kUtil::constOn('ADMIN');
 
 		if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
 			ob_start(); // collect any output from method (other then tags) into buffer
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Init:');
 		}
 
 		if ( !$this->isDebugMode() && !kUtil::constOn('DBG_ZEND_PRESENT') ) {
 			error_reporting(0);
 			ini_set('display_errors', 0);
 		}
 
 		if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) {
 			$error_handler = set_error_handler(Array (&$this, 'handleError'));
 			if ( $error_handler ) {
 				// wrap around previous error handler, if any was set
 				$this->errorHandlers[] = $error_handler;
 			}
 
 			$exception_handler = set_exception_handler(Array (&$this, 'handleException'));
 			if ( $exception_handler ) {
 				// wrap around previous exception handler, if any was set
 				$this->exceptionHandlers[] = $exception_handler;
 			}
 		}
 
 		$this->Factory = new kFactory();
 		$this->registerDefaultClasses();
 
 		$vars = kUtil::parseConfig(true);
 		$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : 'kDBConnection';
 		$this->Conn =& $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array (&$this, 'handleSQLError')));
 		$this->Conn->setup($vars);
 
 		$this->cacheManager =& $this->makeClass('kCacheManager');
 		$this->cacheManager->InitCache();
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Before UnitConfigReader');
 		}
 
 		// init config reader and all managers
 		$this->UnitConfigReader =& $this->makeClass('kUnitConfigReader');
-		$this->UnitConfigReader->scanModules(MODULES_PATH);
+		$this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read
 
 		$this->registerModuleConstants();
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('After UnitConfigReader');
 		}
 
 		define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
 
 		// start processing request
 		$this->HttpQuery =& $this->recallObject('HTTPQuery');
-		$this->HttpQuery->AfterInit();
+		$this->HttpQuery->AfterInit(); // TODO: only uses build-in rewrite listeners, when cache is build for the first time
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
 		}
 
 		$this->Session =& $this->recallObject('Session');
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed Session');
 		}
 
 		$this->UrlManager->LoadStructureTemplateMapping();
 
-		$this->Session->ValidateExpired();
+		$this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
 		}
 
 		$this->cacheManager->LoadApplicationCache();
 
 		$site_timezone = $this->ConfigValue('Config_Site_Time');
 
 		if ( $site_timezone ) {
 			putenv('TZ=' . $site_timezone);
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Loaded cache and phrases');
 		}
 
 		$this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
 
-		$this->UnitConfigReader->AfterConfigRead();
+		$this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed AfterConfigRead');
 		}
 
 		if ( $this->GetVar('m_cat_id') === false ) {
 			$this->SetVar('m_cat_id', 0);
 		}
 
 		if ( !$this->RecallVar('curr_iso') ) {
 			$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
 		}
 
 		$visit_id = $this->RecallVar('visit_id');
 
 		if ( $visit_id !== false ) {
 			$this->SetVar('visits_id', $visit_id);
 		}
 
 		$language =& $this->recallObject('lang.current', null, Array ('live_table' => true));
 		/* @var $language LanguagesItem */
 
 		if ( preg_match('/utf-8/', $language->GetDBField('Charset')) ) {
 			setlocale(LC_ALL, 'en_US.UTF-8');
 			mb_internal_encoding('UTF-8');
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->profileFinish('kernel4_startup');
 		}
 
 		$this->InitDone = true;
 
 		$this->HandleEvent(new kEvent('adm:OnStartup'));
 
 		return true;
 	}
 
 	function InitManagers()
 	{
 		if ($this->InitDone) {
 			throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
 			return ;
 		}
 
 		$this->UrlManager =& $this->makeClass('kUrlManager');
 		$this->EventManager =& $this->makeClass('EventManager');
 		$this->Phrases =& $this->makeClass('kPhraseCache');
 
 		$this->RegisterDefaultBuildEvents();
 	}
 
 	/**
 	 * Returns module information. Searches module by requested field
 	 *
 	 * @param string $field
 	 * @param mixed $value
 	 * @param string $return_field field value to returns, if not specified, then return all fields
 	 * @return Array
 	 */
 	function findModule($field, $value, $return_field = null)
 	{
 		$found = false;
 		foreach ($this->ModuleInfo as $module_name => $module_info) {
 			if ( strtolower($module_info[$field]) == strtolower($value) ) {
 				$found = true;
 				break;
 			}
 		}
 
 		if ( $found ) {
 			return isset($return_field) ? $module_info[$return_field] : $module_info;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Refreshes information about loaded modules
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function refreshModuleInfo()
 	{
 		if (defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules')) {
 			$this->registerModuleConstants();
 			return ;
 		}
 
 		// use makeClass over recallObject, since used before kApplication initialization during installation
 		$modules_helper =& $this->makeClass('ModulesHelper');
 		/* @var $modules_helper kModulesHelper */
 
 		$this->Conn->nextQueryCachable = true;
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'Modules
 				WHERE ' . $modules_helper->getWhereClause() . '
 				ORDER BY LoadOrder';
 		$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
 
 		$this->registerModuleConstants();
 	}
 
 	/**
 	 * Checks if passed language id if valid and sets it to primary otherwise
 	 *
 	 */
 	function VerifyLanguageId()
 	{
 		$language_id = $this->GetVar('m_lang');
 
 		if (!$language_id) {
 			$language_id = 'default';
 		}
 
 		$this->SetVar('lang.current_id', $language_id);
 		$this->SetVar('m_lang', $language_id);
 
 		$lang_mode = $this->GetVar('lang_mode');
 		$this->SetVar('lang_mode', '');
 
 		$lang =& $this->recallObject('lang.current');
 		/* @var $lang kDBItem */
 
 		if (!$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled'))) {
 			if (!defined('IS_INSTALL')) {
 				$this->ApplicationDie('Unknown or disabled language');
 			}
 		}
 
 		$this->SetVar('lang_mode',$lang_mode);
 	}
 
 	/**
 	 * Checks if passed theme id if valid and sets it to primary otherwise
 	 *
 	 */
 	function VerifyThemeId()
 	{
 		if ($this->isAdmin) {
 			kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
 
 			return;
 		}
 
 		$path = $this->GetFrontThemePath();
 
 		if ($path === false) {
 			$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
 		}
 
 		kUtil::safeDefine('THEMES_PATH', $path);
 	}
 
 	function GetFrontThemePath($force=0)
 	{
 		static $path = null;
 
 		if ( !$force && isset($path) ) {
 			return $path;
 		}
 
 		$theme_id = $this->GetVar('m_theme');
 		if ( !$theme_id ) {
 //			$theme_id =  $this->GetDefaultThemeId(1); //1 to force front-end mode!
 			$theme_id = 'default';
 		}
 
 		$this->SetVar('m_theme', $theme_id);
 		$this->SetVar('theme.current_id', $theme_id); // KOSTJA: this is to fool theme' getPassedId
 		$theme =& $this->recallObject('theme.current');
 		/* @var $theme ThemeItem */
 
 		if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) {
 			return false;
 		}
 
 		// assign & then return, since it's static variable
 		$path = '/themes/' . $theme->GetDBField('Name');
 
 		return $path;
 	}
 
 	function GetDefaultLanguageId($init = false)
 	{
 		$cache_key = 'primary_language_info[%LangSerial%]';
 		$language_info = $this->getCache($cache_key);
 
 		if ($language_info === false) {
 			// cache primary language info first
 			$table = $this->getUnitOption('lang', 'TableName');
 			$id_field = $this->getUnitOption('lang', 'IDField');
 
 			$this->Conn->nextQueryCachable = true;
 			$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
 					FROM ' . $table . '
 					WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
 			$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
 
 			if ($language_info !== false) {
 				$this->setCache($cache_key, $language_info);
 			}
 		}
 
 		$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
 
 		if (array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0) {
 			// get from cache
 			return $language_info[$language_key];
 		}
 
 		$language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false;
 
 		if (!$language_id && defined('IS_INSTALL') && IS_INSTALL) {
 			$language_id = 1;
 		}
 
 		return $language_id;
 	}
 
 	function GetDefaultThemeId($force_front=0)
 	{
 		static $theme_id = 0;
 
 		if ($theme_id > 0) {
 			return $theme_id;
 		}
 
 		if (kUtil::constOn('DBG_FORCE_THEME')) {
 			$theme_id = DBG_FORCE_THEME;
 		}
 		elseif (!$force_front && $this->isAdmin) {
 			$theme_id = 999;
 		}
 		else {
 			$cache_key = 'primary_theme[%ThemeSerial%]';
 			$theme_id = $this->getCache($cache_key);
 
 			if ($theme_id === false) {
 				$this->Conn->nextQueryCachable = true;
 				$sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
 						FROM ' . $this->getUnitOption('theme', 'TableName') . '
 						WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
 				$theme_id = $this->Conn->GetOne($sql);
 
 				if ($theme_id !== false) {
 					$this->setCache($cache_key, $theme_id);
 				}
 			}
 		}
 
 		return $theme_id;
 	}
 
 	/**
 	 * Returns site primary currency ISO code
 	 *
 	 * @return string
 	 */
 	function GetPrimaryCurrency()
 	{
 		$cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId');
 		$currency_iso = $this->getCache($cache_key);
 
 		if ($currency_iso === false) {
 			if ($this->isModuleEnabled('In-Commerce')) {
 				$this->Conn->nextQueryCachable = true;
 				$currency_id = $this->siteDomainField('PrimaryCurrencyId');
 
 				$sql = 'SELECT ISO
 						FROM ' . $this->getUnitOption('curr', 'TableName') . '
 						WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1');
 				$currency_iso = $this->Conn->GetOne($sql);
 			}
 			else {
 				$currency_iso = 'USD';
 			}
 
 			$this->setCache($cache_key, $currency_iso);
 		}
 
 		return $currency_iso;
 	}
 
 	/**
 	 * Returns site domain field. When none of site domains are found false is returned.
 	 *
 	 * @param string $field
 	 * @param bool $formatted
 	 * @param string $format
 	 */
 	function siteDomainField($field, $formatted = false, $format = null)
 	{
 		if ($this->isAdmin) {
 			// don't apply any filtering in administrative console
 			return false;
 		}
 
 		if (!$this->siteDomain) {
 			$this->siteDomain =& $this->recallObject('site-domain.current');
 			/* @var $site_domain kDBItem */
 		}
 
 		if ($this->siteDomain->isLoaded()) {
 			return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field);
 		}
 
 		return false;
 	}
 
 	/**
 	* Registers default classes such as ItemController, GridController and LoginController
 	*
 	* Called automatically while initializing Application
 	* @access private
 	* @return void
 	*/
 	function RegisterDefaultClasses()
 	{
 		$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
 		$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
 		$this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php');
 
 		$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager', 'kiCacheable');
 		$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php', null, 'kiCacheable');
 		$this->registerClass('kAgentManager', KERNEL_PATH . '/managers/agent_manager.php', null, 'kiCacheable');
 		$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php');
 
 		$this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php');
 		$this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php');
 		$this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php', null, 'kUrlProcessor');
 		$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php', null, 'kUrlProcessor');
 
 		$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php', null, 'kiCacheable');
 		$this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache');
 		$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
 		$this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php');
 		$this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php');
 
 		$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
 
 		// Params class descendants
 		$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
 		$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
 		$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
 		$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
 		$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery', 'Params');
 
 		// session
 		$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
 		$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
 		$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
 		$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage');
 
 		// template parser
 		$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
 		$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor', 'kTagProcessor');
 		$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php', null, 'kTagProcessor');
 		$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php', null, 'kDBTagProcessor');
 		$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
 		$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php', null, Array ('kHelper', 'kDBTagProcessor'));
 
 		// database
 		$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
 		$this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php');
 		$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
 		$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php', null, 'kDBItem');
 		$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
 		$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php', null, 'kDBList');
 		$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
 		$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php', null, 'kDBEventHandler');
 
 		// email sending
 		$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender', 'kHelper');
 		$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
 
 		// do not move to config - this helper is used before configs are read
 		$this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper');
 	}
 
 	function RegisterDefaultBuildEvents()
 	{
 		$this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
 	}
 
 	/**
 	 * Returns cached category informaton by given cache name. All given category
 	 * information is recached, when at least one of 4 caches is missing.
 	 *
 	 * @param int $category_id
 	 * @param string $name cache name = {filenames, category_designs, category_tree}
 	 * @return string
 	 * @access public
 	 */
 	public function getCategoryCache($category_id, $name)
 	{
 		return $this->cacheManager->getCategoryCache($category_id, $name);
 	}
 
 	/**
 	 * Returns caching type (none, memory, temporary)
 	 *
 	 * @param int $caching_type
 	 * @return bool
 	 * @access public
 	 */
 	public function isCachingType($caching_type)
 	{
 		return $this->cacheManager->isCachingType($caching_type);
 	}
 
 	/**
 	 * Increments serial based on prefix and it's ID (optional)
 	 *
 	 * @param string $prefix
 	 * @param int $id ID (value of IDField) or ForeignKeyField:ID
 	 * @param bool $increment
 	 * @access public
 	 */
 	public function incrementCacheSerial($prefix, $id = null, $increment = true)
 	{
 		return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
 	}
 
 	/**
 	 * Returns cached $key value from cache named $cache_name
 	 *
 	 * @param int $key key name from cache
 	 * @param bool $store_locally store data locally after retrieved
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
 	{
 		return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds);
 	}
 
 	/**
 	 * Adds new value to cache $cache_name and identified by key $key
 	 *
 	 * @param int $key key name to add to cache
 	 * @param mixed $value value of chached record
 	 * @param int $expiration when value expires (0 - doesn't expire)
 	 * @access public
 	 */
 	public function setCache($key, $value, $expiration = 0)
 	{
 		return $this->cacheManager->setCache($key, $value, $expiration);
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 */
 	public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		$this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time);
 	}
 
 	/**
 	 * Deletes key from cache
 	 *
 	 * @param string $key
 	 * @access public
 	 */
 	public function deleteCache($key)
 	{
 		$this->cacheManager->deleteCache($key);
 	}
 
 	/**
 	 * Reset's all memory cache at once
 	 *
 	 * @access public
 	 */
 	public function resetCache()
 	{
 		$this->cacheManager->resetCache();
 	}
 
 	/**
 	 * Returns value from database cache
 	 *
 	 * @param string $name key name
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getDBCache($name, $max_rebuild_seconds = 0)
 	{
 		return $this->cacheManager->getDBCache($name, $max_rebuild_seconds);
 	}
 
 	/**
 	 * Sets value to database cache
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param int|bool $expiration
 	 * @access public
 	 */
 	public function setDBCache($name, $value, $expiration = false)
 	{
 		$this->cacheManager->setDBCache($name, $value, $expiration);
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 */
 	public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		$this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time);
 	}
 
 	/**
 	 * Deletes key from database cache
 	 *
 	 * @param string $name
 	 * @access public
 	 */
 	public function deleteDBCache($name)
 	{
 		$this->cacheManager->deleteDBCache($name);
 	}
 
 	/**
 	 * Registers each module specific constants if any found
 	 *
 	 * @return bool
 	 */
 	function registerModuleConstants()
 	{
 		if ( file_exists(KERNEL_PATH . '/constants.php') ) {
 			kUtil::includeOnce(KERNEL_PATH . '/constants.php');
 		}
 
 		if ( !$this->ModuleInfo ) {
 			return false;
 		}
 
 		foreach ($this->ModuleInfo as $module_info) {
 			$constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
 
 			if ( file_exists($constants_file) ) {
 				kUtil::includeOnce($constants_file);
 			}
 		}
 
 		return true;
 	}
 
 	/**
 	* Actually runs the parser against current template and stores parsing result
 	*
 	* This method gets t variable passed to the script, loads the template given in t variable and
 	* parses it. The result is store in {@link $this->HTML} property.
 	* @access public
 	* @return void
 	*/
 	function Run()
 	{
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Run:');
 		}
 
 		if ( $this->isAdminUser ) {
 			// for permission checking in events & templates
 			$this->LinkVar('module');				// for common configuration templates
 			$this->LinkVar('module_key');			// for common search templates
 			$this->LinkVar('section');				// for common configuration templates
 
 			if ($this->GetVar('m_opener') == 'p') {
 				$this->LinkVar('main_prefix');		// window prefix, that opened selector
 				$this->LinkVar('dst_field');		// field to set value choosed in selector
 			}
 
 			if ($this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax')) {
 				// hide debug output from ajax requests automatically
 				kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
 			}
 		}
 		elseif ( $this->GetVar('admin') ) {
 			// viewing front-end through admin's frame
 			$admin_session =& $this->recallObject('Session.admin');
 			/* @var $admin_session Session */
 
 			$user = (int)$admin_session->RecallVar('user_id'); // in case, when no valid admin session found
 
 			$perm_helper =& $this->recallObject('PermissionsHelper');
 			/* @var $perm_helper kPermissionsHelper */
 
 			if ( $perm_helper->CheckUserPermission($user, 'CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
 				// user can edit cms blocks
 				$editing_mode = $this->GetVar('editing_mode');
 				define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
 			}
 		}
 
 		kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything
 		$this->Phrases->setPhraseEditing();
 
 		$this->EventManager->ProcessRequest();
 
 		$this->InitParser();
 		$t = $this->GetVar('t');
 
 		if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) {
 			$cms_handler =& $this->recallObject('st_EventHandler');
 			/* @var $cms_handler CategoriesEventHandler */
 
 			$t = ltrim($cms_handler->GetDesignTemplate(), '/');
 
 			if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 				$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
 			}
 		}
 		/*else {
 			$cms_handler->SetCatByTemplate();
 		}*/
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Parsing:');
 		}
 
 		$this->HTML = $this->Parser->Run($t);
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application after Parsing:');
 		}
 	}
 
 	function InitParser($theme_name = false)
 	{
 		if( !is_object($this->Parser) ) {
 			$this->Parser =& $this->recallObject('NParser');
 			$this->TemplatesCache =& $this->recallObject('TemplatesCache');
 		}
 
 		$this->TemplatesCache->forceThemeName = $theme_name;
 	}
 
 	/**
 	* Send the parser results to browser
 	*
 	* Actually send everything stored in {@link $this->HTML}, to the browser by echoing it.
 	* @access public
 	* @return void
 	*/
 	function Done()
 	{
 		$this->HandleEvent( new kEvent('adm:OnBeforeShutdown') );
 
 		$debug_mode = defined('DEBUG_MODE') && $this->isDebugMode();
 
 		if ($debug_mode && kUtil::constOn('DBG_PROFILE_MEMORY')) {
 			$this->Debugger->appendMemoryUsage('Application before Done:');
 		}
 
 		if ($debug_mode) {
 			$this->EventManager->runAgents(reAFTER);
 			$this->Session->SaveData();
 
 			if (kUtil::constOn('DBG_CACHE')) {
 				$this->cacheManager->printStatistics();
 			}
 
 			$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
 		}
 		else {
 			// send "Set-Cookie" header before any output is made
 			$this->Session->SetSession();
 
 			$this->HTML = ob_get_clean() . $this->HTML;
 		}
 
 		if ($this->UseOutputCompression()) {
 			$compression_level = $this->ConfigValue('OutputCompressionLevel');
 
 			if (!$compression_level || $compression_level < 0 || $compression_level > 9) {
 				$compression_level = 7;
 			}
 
 			header('Content-Encoding: gzip');
 			echo gzencode($this->HTML, $compression_level);
 		}
 		else {
 			echo $this->HTML;
 		}
 
 		$this->cacheManager->UpdateApplicationCache();
 		flush();
 
 		if (!$debug_mode) {
 			$this->EventManager->runAgents(reAFTER);
 			$this->Session->SaveData();
 		}
 
 		if (defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin) {
 			$this->_storeStatistics();
 		}
 	}
 
 	/**
 	 * Stores script execution statistics to database
 	 *
 	 */
 	function _storeStatistics()
 	{
 		global $start;
 
 		$script_time = microtime(true) - $start;
 		$query_statistics = $this->Conn->getQueryStatistics(); // time & count
 
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'StatisticsCapture
 				WHERE TemplateName = ' . $this->Conn->qstr( $this->GetVar('t') );
 		$data = $this->Conn->GetRow($sql);
 
 		if ($data) {
 			$this->_updateAverageStatistics($data, 'ScriptTime', $script_time);
 			$this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']);
 			$this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']);
 
 			$data['Hits']++;
 			$data['LastHit'] = adodb_mktime();
 
 			$this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']);
 		}
 		else {
 			$data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time;
 			$data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time'];
 			$data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count'];
 			$data['TemplateName'] = $this->GetVar('t');
 			$data['Hits'] = 1;
 			$data['LastHit'] = adodb_mktime();
 			$this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture');
 		}
 	}
 
 	/**
 	 * Calculates average time for statistics
 	 *
 	 * @param Array $data
 	 * @param string $field_prefix
 	 * @param float $current_value
 	 */
 	function _updateAverageStatistics(&$data, $field_prefix, $current_value)
 	{
 		$data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1);
 
 		if ($current_value < $data[$field_prefix . 'Min']) {
 			$data[$field_prefix . 'Min'] = $current_value;
 		}
 
 		if ($current_value > $data[$field_prefix . 'Max']) {
 			$data[$field_prefix . 'Max'] = $current_value;
 		}
 	}
 
 	function logSlowQuery($slow_sql, $time)
 	{
 		$query_crc = crc32($slow_sql);
 
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'SlowSqlCapture
 				WHERE QueryCrc = ' . $query_crc;
 		$data = $this->Conn->Query($sql, null, true);
 
 		if ($data) {
 			$this->_updateAverageStatistics($data, 'Time', $time);
 
 			$template_names = explode(',', $data['TemplateNames']);
 			array_push($template_names, $this->GetVar('t'));
 			$data['TemplateNames'] = implode(',', array_unique($template_names));
 
 			$data['Hits']++;
 			$data['LastHit'] = adodb_mktime();
 
 			$this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']);
 		}
 		else {
 			$data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time;
 			$data['SqlQuery'] = $slow_sql;
 			$data['QueryCrc'] = $query_crc;
 			$data['TemplateNames'] = $this->GetVar('t');
 			$data['Hits'] = 1;
 			$data['LastHit'] = adodb_mktime();
 
 			$this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture');
 		}
 	}
 
 	/**
 	 * Checks if output compression options is available
 	 *
 	 * @return string
 	 */
 	function UseOutputCompression()
 	{
 		if (kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION')) {
 			return false;
 		}
 
 		return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
 	}
 
 	//	Facade
 
 	/**
 	* Returns current session id (SID)
 	* @access public
 	* @return int
 	*/
 	function GetSID()
 	{
 		$session =& $this->recallObject('Session');
 		/* @var $session Session */
 
 		return $session->GetID();
 	}
 
 	function DestroySession()
 	{
 		$session =& $this->recallObject('Session');
 		/* @var $session Session */
 
 		$session->Destroy();
 	}
 
 	/**
 	 * Returns variable passed to the script as GET/POST/COOKIE
 	 *
 	 * @param string $name Name of variable to retrieve
 	 * @param mixed $default default value returned in case if variable not present
 	 *
 	 * @return mixed
 	 * @access public
 	 */
 	public function GetVar($name, $default = false)
 	{
 		return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default;
 	}
 
 	/**
 	 * Returns variable passed to the script as $type
 	 *
 	 * @param string $name Name of variable to retrieve
 	 * @param string $type Get/Post/Cookie
 	 * @param mixed $default default value returned in case if variable not present
 	 *
 	 * @return mixed
 	 * @access public
 	 */
 	public function GetVarDirect($name, $type, $default = false)
 	{
 //		$type = ucfirst($type);
 
 		return isset($this->HttpQuery->$type[$name]) ? $this->HttpQuery->$type[$name] : $default;
 	}
 
 	/**
 	* Returns ALL variables passed to the script as GET/POST/COOKIE
 	*
 	* @access public
 	* @return array
 	*/
 	function GetVars()
 	{
 		return $this->HttpQuery->GetParams();
 	}
 
 	/**
 	* Set the variable 'as it was passed to the script through GET/POST/COOKIE'
 	*
 	* This could be useful to set the variable when you know that
 	* other objects would relay on variable passed from GET/POST/COOKIE
 	* or you could use SetVar() / GetVar() pairs to pass the values between different objects.<br>
 	*
 	* This method is formerly known as $this->Session->SetProperty.
 	* @param string $var Variable name to set
 	* @param mixed $val Variable value
 	* @access public
 	* @return void
 	*/
 	public function SetVar($var,$val)
 	{
 		$this->HttpQuery->Set($var, $val);
 	}
 
 	/**
 	 * Deletes kHTTPQuery variable
 	 *
 	 * @param string $var
 	 * @todo think about method name
 	 */
 	function DeleteVar($var)
 	{
 		return $this->HttpQuery->Remove($var);
 	}
 
 	/**
 	 * Deletes Session variable
 	 *
 	 * @param string $var
 	 */
 	function RemoveVar($var)
 	{
 		return $this->Session->RemoveVar($var);
 	}
 
 	function RemovePersistentVar($var)
 	{
 		return $this->Session->RemovePersistentVar($var);
 	}
 
 	/**
 	 * Restores Session variable to it's db version
 	 *
 	 * @param string $var
 	 */
 	function RestoreVar($var)
 	{
 		return $this->Session->RestoreVar($var);
 	}
 
 	/**
 	* Returns session variable value
 	*
 	* Return value of $var variable stored in Session. An optional default value could be passed as second parameter.
 	*
 	* @see SimpleSession
 	* @access public
 	* @param string $var Variable name
 	* @param mixed $default Default value to return if no $var variable found in session
 	* @return mixed
 	*/
 	function RecallVar($var,$default=false)
 	{
 		return $this->Session->RecallVar($var,$default);
 	}
 
 	function RecallPersistentVar($var, $default = false)
 	{
 		return $this->Session->RecallPersistentVar($var, $default);
 	}
 
 	/**
 	 * Stores variable $val in session under name $var
 	 *
 	 * Use this method to store variable in session. Later this variable could be recalled.
 	 *
 	 * @param string $var Variable name
 	 * @param mixed $val Variable value
 	 * @param bool $optional
 	 * @see kApplication::RecallVar()
 	 * @access public
 	 */
 	function StoreVar($var, $val, $optional = false)
 	{
 		$session =& $this->recallObject('Session');
 		$this->Session->StoreVar($var, $val, $optional);
 	}
 
 	function StorePersistentVar($var, $val, $optional = false)
 	{
 		$this->Session->StorePersistentVar($var, $val, $optional);
 	}
 
 	function StoreVarDefault($var, $val, $optional=false)
 	{
 		$session =& $this->recallObject('Session');
 		$this->Session->StoreVarDefault($var, $val, $optional);
 	}
 
 	/**
 	* Links HTTP Query variable with session variable
 	*
 	* If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session.
 	* This method could be used for making sure that GetVar will return query or session value for given
 	* variable, when query variable should overwrite session (and be stored there for later use).<br>
 	* This could be used for passing item's ID into popup with multiple tab -
 	* in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id').
 	* After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session
 	* @access public
 	* @param string $var HTTP Query (GPC) variable name
 	* @param mixed $ses_var Session variable name
 	* @param mixed $default Default variable value
 	* @param bool $optional
 	*/
 	function LinkVar($var, $ses_var = null, $default = '', $optional = false)
 	{
 		if (!isset($ses_var)) $ses_var = $var;
 		if ($this->GetVar($var) !== false) {
 			$this->StoreVar($ses_var, $this->GetVar($var), $optional);
 		}
 		else {
 			$this->SetVar($var, $this->RecallVar($ses_var, $default));
 		}
 	}
 
 	/**
 	* Returns variable from HTTP Query, or from session if not passed in HTTP Query
 	*
 	* The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed.
 	* Returns the default value if variable does not exist in session and was not passed in HTTP Query
 	*
 	* @see LinkVar
 	* @access public
 	* @param string $var HTTP Query (GPC) variable name
 	* @param mixed $ses_var Session variable name
 	* @param mixed $default Default variable value
 	* @return mixed
 	*/
 	function GetLinkedVar($var, $ses_var = null, $default = '')
 	{
 		$this->LinkVar($var, $ses_var, $default);
 		return $this->GetVar($var);
 	}
 
 	function ProcessParsedTag($prefix, $tag, $params)
 	{
 		$processor = $this->Parser->GetProcessor($prefix);
 		/* @var $processor kDBTagProcessor */
 
 		return $processor->ProcessParsedTag($tag, $params, $prefix);
 	}
 
 	/**
 	* Return ADODB Connection object
 	*
 	* Returns ADODB Connection object already connected to the project database, configurable in config.php
 	* @access public
 	* @return kDBConnection
 	*/
 	function &GetADODBConnection()
 	{
 		return $this->Conn;
 	}
 
 	/**
 	 * Allows to parse given block name or include template
 	 *
 	 * @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
 	 * @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter.
 	 * @param bool $as_template
 	 * @return string
 	 */
 	function ParseBlock($params, $pass_params = false, $as_template = false)
 	{
 		if (substr($params['name'], 0, 5) == 'html:') {
 			return substr($params['name'], 5);
 		}
 
 		return $this->Parser->ParseBlock($params, $pass_params, $as_template);
 	}
 
 	/**
 	 * Checks, that we have given block defined
 	 *
 	 * @param string $name
 	 * @return bool
 	 */
 	function ParserBlockFound($name)
 	{
 		return $this->Parser->blockFound($name);
 	}
 
 	/**
 	 * Allows to include template with a given name and given parameters
 	 *
 	 * @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
 	 * @return string
 	 */
 	function IncludeTemplate($params)
 	{
 		return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0);
 	}
 
 	/**
 	 * 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 = null, $index_file = null)
 	{
 		return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
 	}
 
 	/**
 	 * 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)
 	{
 		return $this->UrlManager->getPhysicalTemplate($seo_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 $this->UrlManager->getVirtualPageTemplate($page_id);
 	}
 
 	/**
 	 * Returns variables with values that should be passed through with this link + variable list
 	 *
 	 * @param Array $params
 	 * @return Array
 	 */
 	function getPassThroughVariables(&$params)
 	{
 		return $this->UrlManager->getPassThroughVariables($params);
 	}
 
 	public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true)
 	{
 		return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var);
 	}
 
 	/**
 	 * Process QueryString only, create
 	 * events, ids, based on config
 	 * set template name and sid in
 	 * desired application variables.
 	 *
 	 * @param string $env_var environment string value
 	 * @param string $pass_name
 	 * @return Array
 	 * @access public
 	 */
 	public function processQueryString($env_var, $pass_name = 'passed')
 	{
 		return $this->UrlManager->plain->parse($env_var, $pass_name);
 	}
 
 	function BaseURL($prefix = '', $ssl = null, $add_port = true)
 	{
 		if ($ssl === null) {
 			// stay on same encryption level
 			return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
 		}
 
 		if ($ssl) {
 			// going from http:// to https://
 			$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
 
 			if (!$base_url) {
 				$ssl_url = $this->siteDomainField('SSLUrl');
 				$base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL');
 			}
 
 			return rtrim($base_url, '/') . $prefix . '/';
 		}
 
 		// going from https:// to http://
 		$domain = $this->siteDomainField('DomainName');
 
 		if ($domain === false) {
 			$domain = DOMAIN;
 		}
 
 		return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
 	}
 
 	function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
 	{
 		$js_redirect = getArrayValue($params, 'js_redirect');
 
 		if ($t == '' || $t === true) {
 			$t = $this->GetVar('t');
 		}
 
 		// pass prefixes and special from previous url
 		if (array_key_exists('js_redirect', $params)) {
 			unset($params['js_redirect']);
 		}
 
 		// allows to send custom responce code along with redirect header
 		if (array_key_exists('response_code', $params)) {
 			$response_code = (int)$params['response_code'];
 			unset($params['response_code']);
 		}
 		else {
 			$response_code = 302; // Found
 		}
 
 		if (!array_key_exists('pass', $params)) {
 			$params['pass'] = 'all';
 		}
 
 		if ($this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t')) {
 			// redirects to the same template as current
 			$params['ajax'] = 'yes';
 		}
 
 		$params['__URLENCODE__'] = 1;
 		$location = $this->HREF($t, $prefix, $params, $index_file);
 
 		if ($this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount))) {
 			$this->Debugger->appendTrace();
 			echo '<strong>Debug output above !!!</strong><br/>' . "\n";
 
 			if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
 				echo 'Referer: <strong>' . $_SERVER['HTTP_REFERER'] . '</strong><br/>' . "\n";
 			}
 
 			echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n";
 		}
 		else {
 			if ($js_redirect) {
 				// show "redirect" template instead of redirecting,
 				// because "Set-Cookie" header won't work, when "Location"
 				// header is used later
 				$this->SetVar('t', 'redirect');
 				$this->SetVar('redirect_to', $location);
 
 				// make all additional parameters available on "redirect" template too
 				foreach ($params as $name => $value) {
 					$this->SetVar($name, $value);
 				}
 
 				return true;
 			}
 			else {
 				if ($this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t')) {
 					// redirection to other then current template during ajax request
 					kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
 					echo '#redirect#' . $location;
 				}
 				elseif (headers_sent() != '') {
 					// some output occured -> redirect using javascript
 					echo '<script type="text/javascript">window.location.href = \'' . $location . '\';</script>';
 				}
 				else {
 					// no output before -> redirect using HTTP header
 
 //					header('HTTP/1.1 302 Found');
 					header('Location: ' . $location, true, $response_code);
 				}
 			}
 		}
 
 		// session expiration is called from session initialization,
 		// that's why $this->Session may be not defined here
 		$session =& $this->recallObject('Session');
 		/* @var $session Session */
 
 		$this->HandleEvent( new kEvent('adm:OnBeforeShutdown') );
 		$session->SaveData();
 
 		ob_end_flush();
 		exit;
 	}
 
 	function Phrase($label, $allow_editing = true, $use_admin = false)
 	{
 		return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin);
 	}
 
 	/**
 	 * Replace language tags in exclamation marks found in text
 	 *
 	 * @param string $text
 	 * @param bool $force_escape force escaping, not escaping of resulting string
 	 * @return string
 	 * @access public
 	 */
 	function ReplaceLanguageTags($text, $force_escape = null)
 	{
 		return $this->Phrases->ReplaceLanguageTags($text, $force_escape);
 	}
 
 	/**
 	 * Checks if user is logged in, and creates
 	 * user object if so. User object can be recalled
 	 * later using "u.current" prefix_special. Also you may
 	 * get user id by getting "u.current_id" variable.
 	 *
 	 * @access private
 	 */
 	function ValidateLogin()
 	{
 		$session =& $this->recallObject('Session');
 		/* @var $session Session */
 
 		$user_id = $session->GetField('PortalUserId');
 
 		if ( !$user_id && $user_id != USER_ROOT ) {
 			$user_id = USER_GUEST;
 		}
 
 		$this->SetVar('u.current_id', $user_id);
 
 		if ( !$this->isAdmin ) {
 			// needed for "profile edit", "registration" forms ON FRONT ONLY
 			$this->SetVar('u_id', $user_id);
 		}
 
 		$this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional
 
 		$this->isAdminUser = $this->isAdmin && $this->LoggedIn();
 
 		if ( $this->GetVar('expired') == 1 ) {
 			// this parameter is set only from admin
 			$user =& $this->recallObject('u.login-admin', null, Array ('form_name' => 'login'));
 			/* @var $user UsersItem */
 
 			$user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired');
 		}
 
 		if ( ($user_id != USER_GUEST) && defined('DBG_REQUREST_LOG') && DBG_REQUREST_LOG ) {
 			$this->HttpQuery->writeRequestLog(DBG_REQUREST_LOG);
 		}
 
 		if ( $user_id != USER_GUEST ) {
 			// normal users + root
 			$this->LoadPersistentVars();
 		}
 	}
 
 	/**
 	 * Loads current user persistent session data
 	 *
 	 */
 	function LoadPersistentVars()
 	{
 		$this->Session->LoadPersistentVars();
 	}
 
 	/**
 	 * Returns configuration option value by name
 	 *
 	 * @param string $name
 	 * @return string
 	 */
 	function ConfigValue($name)
 	{
 		return $this->cacheManager->ConfigValue($name);
 	}
 
 	function SetConfigValue($name, $value)
 	{
 		return $this->cacheManager->SetConfigValue($name, $value);
 	}
 
 	/**
 	 * Allows to process any type of event
 	 *
 	 * @param kEvent $event
 	 * @param Array $params
 	 * @param Array $specific_params
 	 * @access public
 	 * @author Alex
 	 */
 	function HandleEvent(&$event, $params = null, $specific_params = null)
 	{
 		if ( isset($params) ) {
 			$event = new kEvent($params, $specific_params);
 		}
 
 		$this->EventManager->HandleEvent($event);
 	}
 
 	/**
 	 * Registers new class in the factory
 	 *
 	 * @param string $real_class Real name of class as in class declaration
 	 * @param string $file Filename in what $real_class is declared
 	 * @param string $pseudo_class Name under this class object will be accessed using getObject method
 	 * @param Array $dependecies List of classes required for this class functioning
 	 * @access public
 	 * @author Alex
 	 */
 	function registerClass($real_class, $file, $pseudo_class = null, $dependecies = Array() )
 	{
 		$this->Factory->registerClass($real_class, $file, $pseudo_class, $dependecies);
 	}
 
 	/**
 	 * Unregisters existing class from factory
 	 *
 	 * @param string $real_class Real name of class as in class declaration
 	 * @param string $pseudo_class Name under this class object is accessed using getObject method
 	 */
 	function unregisterClass($real_class, $pseudo_class = null)
 	{
 		$this->Factory->unregisterClass($real_class, $pseudo_class);
 	}
 
 	/**
 	 * Add $class_name to required classes list for $depended_class class.
 	 * All required class files are included before $depended_class file is included
 	 *
 	 * @param string $depended_class
 	 * @param string $class_name
 	 * @author Alex
 	 */
 	function registerDependency($depended_class, $class_name)
 	{
 		$this->Factory->registerDependency($depended_class, $class_name);
 	}
 
 	/**
 	 * Add new agent
 	 *
 	 * @param string $short_name name to be used to store last maintenace run info
 	 * @param string $event_name
 	 * @param int $run_interval run interval in seconds
 	 * @param int $type before or after agent
 	 * @param int $status
 	 * @access public
 	 */
 	public function registerAgent($short_name, $event_name, $run_interval, $type = reBEFORE, $status = STATUS_ACTIVE)
 	{
 		$this->EventManager->registerAgent($short_name, $event_name, $run_interval, $type, $status);
 	}
 
 	/**
 	 * Registers Hook from subprefix event to master prefix event
 	 *
 	 * Pattern: Observer
 	 *
 	 * @param string $hook_event
 	 * @param string $do_event
 	 * @param int $mode
 	 * @param bool $conditional
 	 * @access public
 	 */
 	public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false)
 	{
 		$this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional);
 	}
 
 	/**
 	 * Registers build event for given pseudo class
 	 *
 	 * @param string $pseudo_class
 	 * @param string $event_name
 	 * @access public
 	 */
 	public function registerBuildEvent($pseudo_class, $event_name)
 	{
 		$this->EventManager->registerBuildEvent($pseudo_class, $event_name);
 	}
 
 	/**
 	 * Allows one TagProcessor tag act as other TagProcessor tag
 	 *
 	 * @param Array $tag_info
 	 * @author Kostja
 	 */
 	function registerAggregateTag($tag_info)
 	{
 		$aggregator =& $this->recallObject('TagsAggregator', 'kArray');
 		/* @var $aggregator kArray */
 
 		$tag_data = Array(
 			$tag_info['LocalPrefix'],
 			$tag_info['LocalTagName'],
 			getArrayValue($tag_info, 'LocalSpecial')
 		);
 
 		$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data);
 	}
 
 	/**
 	 * Returns object using params specified, creates it if is required
 	 *
 	 * @param string $name
 	 * @param string $pseudo_class
 	 * @param Array $event_params
 	 * @param Array $arguments
 	 * @return kBase
 	 */
 	public function &recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
 	{
 		/*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) {
 			// first time, when object with "_prefix_here_" prefix is accessed
 			$this->Debugger->appendTrace();
 		}*/
 
 		$result =& $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
 
 		return $result;
 	}
 
 	/**
 	 * Returns tag processor for prefix specified
 	 *
 	 * @param string $prefix
 	 * @return kDBTagProcessor
 	 */
 	function &recallTagProcessor($prefix)
 	{
 		$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
 		$result =& $this->recallObject($prefix . '_TagProcessor');
 
 		return $result;
 	}
 
 	/**
 	 * Checks if object with prefix passes was already created in factory
 	 *
 	 * @param string $name object presudo_class, prefix
 	 * @return bool
 	 * @author Kostja
 	 */
 	function hasObject($name)
 	{
 		return isset($this->Factory->Storage[$name]);
 	}
 
 	/**
 	 * Removes object from storage by given name
 	 *
 	 * @param string $name Object's name in the Storage
 	 * @author Kostja
 	 */
 	public function removeObject($name)
 	{
 		$this->Factory->DestroyObject($name);
 	}
 
 	/**
 	 * Get's real class name for pseudo class, includes class file and creates class instance
 	 *
 	 * Pattern: Factory Method
 	 *
 	 * @param string $pseudo_class
 	 * @param Array $arguments
 	 * @return kBase
 	 * @access public
 	 */
 	public function &makeClass($pseudo_class, $arguments = Array ())
 	{
 		$result =& $this->Factory->makeClass($pseudo_class, $arguments);
 
 		return $result;
 	}
 
 	/**
 	 * Checks if application is in debug mode
 	 *
 	 * @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
 	 * @return bool
 	 * @author Alex
 	 * @access public
 	 */
 	public function isDebugMode($check_debugger = true)
 	{
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		if ($check_debugger) {
 			$debug_mode = $debug_mode && is_object($this->Debugger);
 		}
 		return $debug_mode;
 	}
 
 	/**
 	 * Apply url rewriting used by mod_rewrite or not
 	 *
 	 * @param bool|null $ssl Force ssl link to be build
 	 * @return bool
 	 */
 	function RewriteURLs($ssl = false)
 	{
 		// case #1,#4:
 		//			we want to create https link from http mode
 		//			we want to create https link from https mode
 		//			conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
 
 		// case #2,#3:
 		//			we want to create http link from https mode
 		//			we want to create http link from http mode
 		//			conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
 
 		$allow_rewriting =
 			(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
 			|| // or allow rewriting for redirect TO httpS or when already in httpS
 			(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
 
 		return kUtil::constOn('MOD_REWRITE') && $allow_rewriting;
 	}
 
 	/**
 	 * Reads unit (specified by $prefix)
 	 * option specified by $option
 	 *
 	 * @param string $prefix
 	 * @param string $option
 	 * @param mixed $default
 	 * @return string
 	 * @access public
 	 */
 	public function getUnitOption($prefix, $option, $default = false)
 	{
 		return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
 	}
 
 	/**
 	 * Set's new unit option value
 	 *
 	 * @param string $prefix
 	 * @param string $option
 	 * @param string $value
 	 * @access public
 	 */
 	public function setUnitOption($prefix, $option, $value)
 	{
 		$this->UnitConfigReader->setUnitOption($prefix,$option,$value);
 	}
 
 	/**
 	 * Read all unit with $prefix options
 	 *
 	 * @param string $prefix
 	 * @return Array
 	 * @access public
 	 */
 	public function getUnitOptions($prefix)
 	{
 		return $this->UnitConfigReader->getUnitOptions($prefix);
 	}
 
 	/**
 	 * Returns true if config exists and is allowed for reading
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
 	public function prefixRegistred($prefix)
 	{
 		return $this->UnitConfigReader->prefixRegistred($prefix);
 	}
 
 	/**
 	 * Splits any mixing of prefix and
 	 * special into correct ones
 	 *
 	 * @param string $prefix_special
 	 * @return Array
 	 * @access public
 	 */
 	public function processPrefix($prefix_special)
 	{
 		return $this->Factory->processPrefix($prefix_special);
 	}
 
 	/**
 	 * Set's new event for $prefix_special
 	 * passed
 	 *
 	 * @param string $prefix_special
 	 * @param string $event_name
 	 * @access public
 	 */
 	function setEvent($prefix_special, $event_name)
 	{
 		$this->EventManager->setEvent($prefix_special,$event_name);
 	}
 
 	/**
 	 * SQL Error Handler
 	 *
 	 * @param int $code
 	 * @param string $msg
 	 * @param string $sql
 	 * @return bool
 	 * @access private
 	 * @author Alex
 	 */
 	function handleSQLError($code, $msg, $sql)
 	{
 		if ( isset($this->Debugger) ) {
 			$long_error_msg = '<span class="debug_error">' . $msg . ' (' . $code . ')</span><br/><a href="javascript:$Debugger.SetClipboard(\'' . htmlspecialchars($sql) . '\');"><strong>SQL</strong></a>: ' . $this->Debugger->formatSQL($sql);
 			$long_id = $this->Debugger->mapLongError($long_error_msg);
 			$error_msg = mb_substr($msg . ' (' . $code . ') [' . $sql . ']', 0, 1000) . ' #' . $long_id;
 
 			if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) {
 				throw new Exception($error_msg);
 			}
 			else {
 				$this->Debugger->appendTrace();
 			}
 		}
 		else {
 			// when not debug mode, then fatal database query won't break anything
 			$error_msg = '<strong>SQL Error</strong> in sql: ' . $sql . ', code <strong>' . $code . '</strong> (' . $msg . ')';
 		}
 
 		trigger_error($error_msg, E_USER_WARNING);
 
 		return true;
 	}
 
 	/**
 	 * Default error handler
 	 *
 	 * @param int $errno
 	 * @param string $errstr
 	 * @param string $errfile
 	 * @param int $errline
 	 * @param Array $errcontext
 	 * @return bool
 	 * @access public
 	 */
 	public function handleError($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
 	{
 		$this->errorLogSilent($errno, $errstr, $errfile, $errline);
 
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
 
 		if ( !$this->errorHandlers || ($debug_mode && $skip_reporting) ) {
 			// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
 			if ( $errno == E_USER_ERROR ) {
 				$this->errorDisplayFatal('<strong>Fatal Error: </strong>' . "{$errstr} in {$errfile} on line {$errline}");
 			}
 
 			if ( !$this->errorHandlers ) {
 				return true;
 			}
 		}
 
 		$res = false;
 		/* @var $handler Closure */
 
 		foreach ($this->errorHandlers as $handler) {
 			if ( is_array($handler) ) {
 				$object =& $handler[0];
 				$method = $handler[1];
 				$res = $object->$method($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 			else {
 				$res = $handler($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 		}
 
 		return $res;
 	}
 
 	/**
 	 * Handles exception
 	 *
 	 * @param Exception $exception
 	 * @return bool
 	 * @access public
 	 */
 	public function handleException($exception)
 	{
 		// transform exception to regular error (no need to rewrite existing error handlers)
 		$errno = $exception->getCode();
 		$errstr = $exception->getMessage();
 		$errfile = $exception->getFile();
 		$errline = $exception->getLine();
 
 		$this->errorLogSilent($errno, $errstr, $errfile, $errline);
 
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
 
 		if ( !$this->exceptionHandlers || ($debug_mode && $skip_reporting) ) {
 			// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
 			$this->errorDisplayFatal('<strong>' . get_class($exception) . ': </strong>' . "{$errstr} in {$errfile} on line {$errline}");
 
 			if ( !$this->exceptionHandlers ) {
 				return true;
 			}
 		}
 
 		$res = false;
 		/* @var $handler Closure */
 
 		foreach ($this->exceptionHandlers as $handler) {
 			if ( is_array($handler) ) {
 				$object =& $handler[0];
 				$method = $handler[1];
 				$res = $object->$method($exception);
 			}
 			else {
 				$res = $handler($exception);
 			}
 		}
 
 		return $res;
 	}
 
 	/**
 	 * Silently saves each given error message to "silent_log.txt" file, when silent log mode is enabled
 	 * @param int $errno
 	 * @param string $errstr
 	 * @param string $errfile
 	 * @param int $errline
 	 * @return void
 	 * @access protected
 	 */
 	protected function errorLogSilent($errno, $errstr = '', $errfile = '', $errline = null)
 	{
 		if ( !defined('SILENT_LOG') || !SILENT_LOG ) {
 			return;
 		}
 
 		if ( !(defined('DBG_IGNORE_STRICT_ERRORS') && DBG_IGNORE_STRICT_ERRORS && defined('E_STRICT') && ($errno == E_STRICT)) ) {
 			$time = adodb_date('d/m/Y H:i:s');
 
 			$fp = fopen((defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/silent_log.txt', 'a');
 			fwrite($fp, '[' . $time . '] #' . $errno . ': ' . strip_tags($errstr) . ' in [' . $errfile . '] on line ' . $errline . "\n");
 			fclose($fp);
 		}
 	}
 
 	/**
 	 * Displays div with given error message
 	 *
 	 * @param string $msg
 	 * @return void
 	 * @access protected
 	 */
 	protected function errorDisplayFatal($msg)
 	{
 		$margin = $this->isAdmin ? '8px' : 'auto';
 		echo '<div style="background-color: #FEFFBF; margin: ' . $margin . '; padding: 10px; border: 2px solid red; text-align: center">' . $msg . '</div>';
 		exit;
 	}
 
 	/**
 	 * Prints trace, when debug mode is not available
 	 *
 	 * @param bool $return_result
 	 * @param int $skip_levels
 	 * @return string
 	 * @access public
 	 */
 	public function printTrace($return_result = false, $skip_levels = 1)
 	{
 		$ret = Array ();
 		$trace = debug_backtrace(false);
 
 		for ($i = 0; $i < $skip_levels; $i++) {
 			array_shift($trace);
 		}
 
 		foreach ($trace as $level => $trace_info) {
 			if ( isset($trace_info['class']) ) {
 				$object = $trace_info['class'];
 			}
 			elseif ( isset($trace_info['object']) ) {
 				$object = get_class($trace_info['object']);
 			}
 			else {
 				$object = '';
 			}
 
 			$args = '';
 			$type = isset($trace_info['type']) ? $trace_info['type'] : '';
 
 			if ( isset($trace_info['args']) ) {
 				foreach ($trace_info['args'] as $argument) {
 					if ( is_object($argument) ) {
 						$args .= get_class($argument) . ' instance, ';
 					}
 					else {
 						$args .= is_array($argument) ? 'Array' : substr($argument, 0, 10) . ' ..., ';
 					}
 				}
 
 				$args = substr($args, 0, -2);
 			}
 
 			$ret[] = '#' . $level . '  ' . $object . $type . $trace_info['function'] . '(' . $args . ') called at [' . $trace_info['file'] . ':' . $trace_info['line'] . ']';
 		}
 
 		if ( $return_result ) {
 			return implode("\n", $ret);
 		}
 
 		echo implode("\n", $ret);
 
 		return '';
 	}
 
 	/**
 	 * Returns & blocks next ResourceId available in system
 	 *
 	 * @return int
 	 * @access public
 	 * @author Alex
 	 */
 	function NextResourceId()
 	{
 		$table_name = TABLE_PREFIX.'IdGenerator';
 
 		$this->Conn->Query('LOCK TABLES '.$table_name.' WRITE');
 		$this->Conn->Query('UPDATE '.$table_name.' SET lastid = lastid + 1');
 		$id = $this->Conn->GetOne('SELECT lastid FROM '.$table_name);
 		if($id === false)
 		{
 			$this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)');
 			$id = 2;
 		}
 		$this->Conn->Query('UNLOCK TABLES');
 		return $id - 1;
 	}
 
 	/**
 	 * Returns genealogical main prefix for sub-table prefix passes
 	 * OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix
 	 *
 	 * @param string $current_prefix
 	 * @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
 	 * @return string
 	 * @access public
 	 */
 	function GetTopmostPrefix($current_prefix, $real_top = false)
 	{
 		// 1. get genealogical tree of $current_prefix
 		$prefixes = Array ($current_prefix);
 		while ( $parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix') ) {
 			if (!$this->prefixRegistred($parent_prefix)) {
 				// stop searching, when parent prefix is not registered
 				break;
 			}
 
 			$current_prefix = $parent_prefix;
 			array_unshift($prefixes, $current_prefix);
 		}
 
 		if ($real_top) {
 			return $current_prefix;
 		}
 
 		// 2. find what if parent is passed
 		$passed = explode(',', $this->GetVar('all_passed'));
 		foreach ($prefixes as $a_prefix) {
 			if (in_array($a_prefix, $passed)) {
 				return $a_prefix;
 			}
 		}
 
 		return $current_prefix;
 	}
 
 	/**
 	 * Triggers email event of type Admin
 	 *
 	 * @param string $email_event_name
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 */
 	function &EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
 	{
 		$event =& $this->EmailEvent($email_event_name, EmailEvent::EVENT_TYPE_ADMIN, $to_user_id, $send_params);
 
 		return $event;
 	}
 
 	/**
 	 * Triggers email event of type User
 	 *
 	 * @param string $email_event_name
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 */
 	function &EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
 	{
 		$event =& $this->EmailEvent($email_event_name, EmailEvent::EVENT_TYPE_FRONTEND, $to_user_id, $send_params);
 
 		return $event;
 	}
 
 	/**
 	 * Triggers general email event
 	 *
 	 * @param string $email_event_name
 	 * @param int $email_event_type (0 for User, 1 for Admin)
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params,
 	 *  possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 */
 	function &EmailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
 	{
 		$params = Array (
 			'EmailEventName' => $email_event_name,
 			'EmailEventToUserId' => $to_user_id,
 			'EmailEventType' => $email_event_type,
 			'DirectSendParams' => $send_params,
 		);
 
 		if (array_key_exists('use_special', $send_params)) {
 			$event_str = 'emailevents.' . $send_params['use_special'] . ':OnEmailEvent';
 		}
 		else {
 			$event_str = 'emailevents:OnEmailEvent';
 		}
 
 		$this->HandleEvent($event, $event_str, $params);
 
 		return $event;
 	}
 
 	/**
 	 * Allows to check if user in this session is logged in or not
 	 *
 	 * @return bool
 	 */
 	function LoggedIn()
 	{
 		// no session during expiration process
 		return is_null($this->Session) ? false : $this->Session->LoggedIn();
 	}
 
 	/**
 	 * Check current user permissions based on it's group permissions in specified category
 	 *
 	 * @param string $name permission name
 	 * @param int $cat_id category id, current used if not specified
 	 * @param int $type permission type {1 - system, 0 - per category}
 	 * @return int
 	 */
 	function CheckPermission($name, $type = 1, $cat_id = null)
 	{
 		$perm_helper =& $this->recallObject('PermissionsHelper');
 		/* @var $perm_helper kPermissionsHelper */
 
 		return $perm_helper->CheckPermission($name, $type, $cat_id);
 	}
 
 	/**
 	 * Set's any field of current visit
 	 *
 	 * @param string $field
 	 * @param mixed $value
 	 */
 	function setVisitField($field, $value)
 	{
 		if ($this->isAdmin || !$this->ConfigValue('UseVisitorTracking')) {
 			// admin logins are not registred in visits list
 			return ;
 		}
 
 		$visit =& $this->recallObject('visits', null, Array ('raise_warnings' => 0));
 		/* @var $visit kDBItem */
 
 		if ($visit->isLoaded()) {
 			$visit->SetDBField($field, $value);
 			$visit->Update();
 		}
 	}
 
 	/**
 	 * Allows to check if in-portal is installed
 	 *
 	 * @return bool
 	 */
 	function isInstalled()
 	{
 		return $this->InitDone && (count($this->ModuleInfo) > 0);
 	}
 
 	/**
 	 * Allows to determine if module is installed & enabled
 	 *
 	 * @param string $module_name
 	 * @return bool
 	 */
 	function isModuleEnabled($module_name)
 	{
 		return $this->findModule('Name', $module_name) !== false;
 	}
 
 	/**
 	 * Returns Window ID of passed prefix main prefix (in edit mode)
 	 *
 	 * @param string $prefix
 	 * @return mixed
 	 */
 	function GetTopmostWid($prefix)
 	{
 		$top_prefix = $this->GetTopmostPrefix($prefix);
 		$mode = $this->GetVar($top_prefix.'_mode');
 		return $mode != '' ? substr($mode, 1) : '';
 	}
 
 	/**
 	 * Get temp table name
 	 *
 	 * @param string $table
 	 * @param mixed $wid
 	 * @return string
 	 */
 	function GetTempName($table, $wid = '')
 	{
 		return $this->GetTempTablePrefix($wid) . $table;
 	}
 
 	function GetTempTablePrefix($wid = '')
 	{
 		if (preg_match('/prefix:(.*)/', $wid, $regs)) {
 			$wid = $this->GetTopmostWid($regs[1]);
 		}
 
 		return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
 	}
 
 	function IsTempTable($table)
 	{
 		static $cache = Array ();
 
 		if ( !array_key_exists($table, $cache) ) {
 			$cache[$table] = preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$table);
 		}
 
 		return (bool)$cache[$table];
 	}
 
 	/**
 	 * Checks, that given prefix is in temp mode
 	 *
 	 * @param string $prefix
 	 * @param string $special
 	 * @return bool
 	 */
 	function IsTempMode($prefix, $special = '')
 	{
 		$top_prefix = $this->GetTopmostPrefix($prefix);
 
 		$var_names = Array (
 			$top_prefix,
 			rtrim($top_prefix . '_' . $special, '_'), // from post
 			rtrim($top_prefix . '.' . $special, '.'), // assembled locally
 		);
 
 		$var_names = array_unique($var_names);
 
 		$temp_mode = false;
 		foreach ($var_names as $var_name) {
 			$value = $this->GetVar($var_name . '_mode');
 			if ($value && (substr($value, 0, 1) == 't')) {
 				$temp_mode = true;
 				break;
 			}
 		}
 
 		return $temp_mode;
 	}
 
 	/**
 	 * Return live table name based on temp table name
 	 *
 	 * @param string $temp_table
 	 * @return string
 	 */
 	function GetLiveName($temp_table)
 	{
 		if( preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$temp_table, $rets) )
 		{
 			// cut wid from table end if any
 			return $rets[2];
 		}
 		else
 		{
 			return $temp_table;
 		}
 	}
 
 	function CheckProcessors($processors)
 	{
 		foreach ($processors as $a_processor)
 		{
 			if (!isset($this->CachedProcessors[$a_processor])) {
 				$this->CachedProcessors[$a_processor] =& $this->recallObject($a_processor.'_TagProcessor');
 			}
 		}
 	}
 
 	function ApplicationDie($message = '')
 	{
 		$message = ob_get_clean().$message;
 		if ($this->isDebugMode()) {
 			$message .= $this->Debugger->printReport(true);
 		}
 
 		echo $this->UseOutputCompression() ? gzencode($message, DBG_COMPRESSION_LEVEL) : $message;
 		exit;
 	}
 
 
 	/* moved from MyApplication */
 
 	function getUserGroups($user_id)
 	{
 		switch ($user_id) {
 			case USER_ROOT:
 				$user_groups = $this->ConfigValue('User_LoggedInGroup');
 				break;
 
 			case USER_GUEST:
 				$user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup');
 				break;
 
 			default:
 				$sql = 'SELECT GroupId
 						FROM ' . TABLE_PREFIX . 'UserGroup
 						WHERE PortalUserId = ' . (int)$user_id;
 				$res = $this->Conn->GetCol($sql);
 
 				$user_groups = Array( $this->ConfigValue('User_LoggedInGroup') );
 				if ($res) {
 					$user_groups = array_merge($user_groups, $res);
 				}
 
 				$user_groups = implode(',', $user_groups);
 		}
 
 		return $user_groups;
 	}
 
 
 	/**
 	 * Allows to detect if page is browsed by spider (293 agents supported)
 	 *
 	 * @return bool
 	 */
 	function IsSpider()
 	{
 		static $is_spider = null;
 
 		if (!isset($is_spider)) {
 			$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
 			$robots = file(FULL_PATH.'/core/robots_list.txt');
 			foreach ($robots as $robot_info) {
 				$robot_info = explode("\t", $robot_info, 3);
 				if ($user_agent == trim($robot_info[2])) {
 					$is_spider = true;
 					break;
 				}
 			}
 		}
 
 		return $is_spider;
 	}
 
 	/**
 	 * Allows to detect table's presense in database
 	 *
 	 * @param string $table_name
 	 * @return bool
 	 */
 	function TableFound($table_name)
 	{
 		return $this->Conn->TableFound($table_name);
 	}
 
 	/**
 	 * Returns counter value
 	 *
 	 * @param string $name counter name
 	 * @param Array $params counter parameters
 	 * @param string $query_name specify query name directly (don't generate from parmeters)
 	 * @param bool $multiple_results
 	 * @return mixed
 	 */
 	function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
 	{
 		$count_helper =& $this->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
 	}
 
 	/**
 	 * Resets counter, which are affected by one of specified tables
 	 *
 	 * @param string $tables comma separated tables list used in counting sqls
 	 * @return void
 	 * @access public
 	 */
 	public function resetCounters($tables)
 	{
 		if ( kUtil::constOn('IS_INSTALL') ) {
 			return;
 		}
 
 		$count_helper =& $this->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		$count_helper->resetCounters($tables);
 	}
 
 	/**
 	 * Sends XML header + optionally displays xml heading
 	 *
 	 * @param string|bool $xml_version
 	 * @return string
 	 * @access public
 	 * @author Alex
 	 */
 	public function XMLHeader($xml_version = false)
 	{
 		$lang =& $this->recallObject('lang.current');
 		/* @var $lang LanguagesItem */
 
 		header('Content-type: text/xml; charset=' . $lang->GetDBField('Charset'));
 
 		return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . $lang->GetDBField('Charset') . '"?>' : '';
 	}
 
 	/**
 	 * Returns category tree
 	 *
 	 * @param int $category_id
 	 * @return Array
 	 */
 	function getTreeIndex($category_id)
 	{
 		$tree_index = $this->getCategoryCache($category_id, 'category_tree');
 
 		if ($tree_index) {
 			$ret = Array ();
 			list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
 
 			return $ret;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Base category of all categories
 	 * Usually replaced category, with ID = 0 in category-related operations.
 	 *
 	 * @return int
 	 */
 	function getBaseCategory()
 	{
 		// same, what $this->findModule('Name', 'Core', 'RootCat') does
 		// don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade
 
 		return $this->ModuleInfo['Core']['RootCat'];
 	}
 
-	function DeleteUnitCache($include_sections = false)
+	/**
+	 * Deletes all data, that was cached during unit config parsing (excluding unit config locations)
+	 *
+	 * @param Array $config_variables
+	 * @access public
+	 */
+	public function DeleteUnitCache($config_variables = null)
+	{
+		$this->cacheManager->DeleteUnitCache($config_variables);
+	}
+
+	/**
+	 * Deletes cached section tree, used during permission checking and admin console tree display
+	 *
+	 * @return void
+	 * @access public
+	 */
+	public function DeleteSectionCache()
 	{
-		$this->cacheManager->DeleteUnitCache($include_sections);
+		$this->cacheManager->DeleteSectionCache();
 	}
 
 	/**
 	 * Sets data from cache to object
 	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
 		$this->ReplacementTemplates = $data['Application.ReplacementTemplates'];
 		$this->RewriteListeners = $data['Application.RewriteListeners'];
 		$this->ModuleInfo = $data['Application.ModuleInfo'];
 	}
 
 	/**
 	 * Gets object data for caching
 	 * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
 	 *
 	 * @access public
 	 * @return Array
 	 */
 	public function getToCache()
 	{
 		return 	Array (
 					'Application.ReplacementTemplates' => $this->ReplacementTemplates,
 					'Application.RewriteListeners' => $this->RewriteListeners,
 					'Application.ModuleInfo' => $this->ModuleInfo,
 				);
 	}
 
 	public function delayUnitProcessing($method, $params)
 	{
 		$this->cacheManager->delayUnitProcessing($method, $params);
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/cache_manager.php
===================================================================
--- branches/5.2.x/core/kernel/managers/cache_manager.php	(revision 14786)
+++ branches/5.2.x/core/kernel/managers/cache_manager.php	(revision 14787)
@@ -1,742 +1,752 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 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 kCacheManager extends kBase implements kiCacheable {
 
 	/**
 	 * Used variables from ConfigurationValues table
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $configVariables = Array();
 
 	/**
+	 * Used variables from ConfigurationValues table retrieved from unit cache
+	 *
+	 * @var Array
+	 * @access protected
+	 */
+	protected $originalConfigVariables = Array ();
+
+	/**
 	 * IDs of config variables used in current run (for caching)
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $configIDs = Array ();
 
 	/**
-	 * IDs of config variables retirieved from cache
+	 * IDs of config variables retrieved from unit cache
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $originalConfigIDs = Array ();
 
 	/**
 	 * Object of memory caching class
 	 *
 	 * @var kCache
 	 * @access protected
 	 */
 	protected $cacheHandler = null;
 
 	protected $temporaryCache = Array (
 		'registerAggregateTag' => Array (),
 		'registerAgent' => Array (),
 		'registerHook' => Array (),
 		'registerBuildEvent' => Array (),
 		'registerAggregateTag' => Array (),
 	);
 
 	/**
 	 * Creates caching manager instance
 	 *
 	 * @access public
 	 */
 	public function InitCache()
 	{
 		$this->cacheHandler =& $this->Application->makeClass('kCache');
 	}
 
 	/**
 	 * Returns cache key, used to cache phrase and configuration variable IDs used on current page
 	 *
 	 * @return string
 	 * @access protected
 	 */
 	protected function getCacheKey()
 	{
 		// TODO: maybe language part isn't required, since same phrase from different languages have one ID now
 		return $this->Application->GetVar('t') . $this->Application->GetVar('m_theme') . $this->Application->GetVar('m_lang') . $this->Application->isAdmin;
 	}
 
 	/**
 	 * Loads phrases and configuration variables, that were used on this template last time
 	 *
 	 * @access public
 	 */
 	public function LoadApplicationCache()
 	{
 		$phrase_ids = $config_ids = Array ();
 
 		$sql = 'SELECT PhraseList, ConfigVariables
 				FROM ' . TABLE_PREFIX . 'PhraseCache
 				WHERE Template = ' . $this->Conn->qstr( md5($this->getCacheKey()) );
 		$res = $this->Conn->GetRow($sql);
 
 		if ($res) {
 			if ( $res['PhraseList'] ) {
 				$phrase_ids = explode(',', $res['PhraseList']);
 			}
 
 			if ( $res['ConfigVariables'] ) {
 				$config_ids = array_diff( explode(',', $res['ConfigVariables']), $this->originalConfigIDs);
 			}
 		}
 
 		$this->Application->Phrases->Init('phrases', '', null, $phrase_ids);
 		$this->configIDs = $this->originalConfigIDs = $config_ids;
 
 		$this->InitConfig();
 	}
 
 	/**
 	 * Updates phrases and configuration variables, that were used on this template
 	 *
 	 * @access public
 	 */
 	public function UpdateApplicationCache()
 	{
 		$update = false;
 
 		//something changed
 		$update = $update || $this->Application->Phrases->NeedsCacheUpdate();
 		$update = $update || (count($this->configIDs) && $this->configIDs != $this->originalConfigIDs);
 
 		if ($update) {
 			$fields_hash = Array (
 				'PhraseList' => implode(',', $this->Application->Phrases->Ids),
 				'CacheDate' => adodb_mktime(),
 				'Template' => md5( $this->getCacheKey() ),
 				'ConfigVariables' => implode(',', array_unique($this->configIDs)),
 			);
 
 			$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PhraseCache', 'REPLACE');
 		}
 	}
 
 	/**
 	 * Loads configuration variables, that were used on this template last time
 	 *
 	 * @access protected
 	 */
 	protected function InitConfig()
 	{
 		if (!$this->originalConfigIDs) {
 			return ;
 		}
 
 		$sql = 'SELECT VariableValue, VariableName
 				FROM ' . TABLE_PREFIX . 'ConfigurationValues
 			 	WHERE VariableId IN (' . implode(',', $this->originalConfigIDs) . ')';
 		$config_variables = $this->Conn->GetCol($sql, 'VariableName');
 
 		$this->configVariables = array_merge($this->configVariables, $config_variables);
 	}
 
 	/**
 	 * Returns configuration option value by name
 	 *
 	 * @param string $name
 	 * @return string
 	 * @access public
 	 */
 	public function ConfigValue($name)
 	{
 		if ($name == 'Smtp_AdminMailFrom') {
 			$res = $this->Application->siteDomainField('AdminEmail');
 
 			if ($res) {
 				return $res;
 			}
 		}
 
 		if ( array_key_exists($name, $this->configVariables) ) {
 			return $this->configVariables[$name];
 		}
 
 		if ( defined('IS_INSTALL') && IS_INSTALL && !$this->Application->TableFound('ConfigurationValues') ) {
 			return false;
 		}
 
 		$this->Conn->nextQueryCachable = true;
 		$sql = 'SELECT VariableId, VariableValue
 				FROM ' . TABLE_PREFIX . 'ConfigurationValues
 				WHERE VariableName = ' . $this->Conn->qstr($name);
 		$res = $this->Conn->GetRow($sql);
 
 		if ($res !== false) {
 			$this->configIDs[] = $res['VariableId'];
 			$this->configVariables[$name] = $res['VariableValue'];
 
 			return $res['VariableValue'];
 		}
 
 		trigger_error('Usage of undefined configuration variable "<strong>' . $name . '</strong>"', E_USER_NOTICE);
 
 		return false;
 	}
 
 	function SetConfigValue($name, $value)
 	{
 		$this->configVariables[$name] = $value;
 
 		$fields_hash = Array ('VariableValue' => $value);
 		$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'ConfigurationValues', 'VariableName = ' . $this->Conn->qstr($name));
 
-		$this->DeleteUnitCache();
+		if ( array_key_exists($name, $this->originalConfigVariables) && $value != $this->originalConfigVariables[$name] ) {
+			$this->DeleteUnitCache();
+		}
 	}
 
 	/**
 	 * Loads data, that was cached during unit config parsing
 	 *
 	 * @return bool
 	 * @access public
 	 */
 	public function LoadUnitCache()
 	{
 		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 			$data = $this->Application->getCache('master:configs_parsed', false, CacheSettings::$unitCacheRebuildTime);
 		}
 		else {
 			$data = $this->Application->getDBCache('configs_parsed', CacheSettings::$unitCacheRebuildTime);
 		}
 
 		if ( $data ) {
 			$cache = unserialize($data); // 126 KB all modules
 			unset($data);
 
 			$this->Application->InitManagers();
 
 			$this->Application->Factory->setFromCache($cache);
 			$this->Application->UnitConfigReader->setFromCache($cache);
 			$this->Application->EventManager->setFromCache($cache);
 
 			$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 			/* @var $aggregator kArray */
 
 			$aggregator->setFromCache($cache);
 			$this->setFromCache($cache);
 			$this->Application->setFromCache($cache);
 			unset($cache);
 
 			return true;
 		}
 
 		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 			$this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
 		}
 		else {
 			$this->Application->rebuildDBCache('configs_parsed', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
 		}
 
 		return false;
 	}
 
 	/**
 	 * Empties factory and event manager cache (without storing changes)
 	 */
 	public function EmptyUnitCache()
 	{
 		$cache_keys = Array (
 			'Factory.Files', 'Factory.realClasses', 'Factory.Dependencies',
 			'EventManager.buildEvents', 'EventManager.beforeHooks',
 			'EventManager.afterHooks', 'EventManager.beforeRegularEvents',
 			'EventManager.afterRegularEvents'
 		);
 
 		$empty_cache = Array ();
 
 		foreach ($cache_keys as $cache_key) {
 			$empty_cache[$cache_key] = Array ();
 		}
 
 		$this->Application->Factory->setFromCache($empty_cache);
 		$this->Application->EventManager->setFromCache($empty_cache);
 
 		// otherwise ModulesHelper indirectly used from includeConfigFiles won't work
 		$this->Application->RegisterDefaultClasses();
 	}
 
 	/**
 	 * Updates data, that was parsed from unit configs this time
 	 *
 	 * @access public
 	 */
 	public function UpdateUnitCache()
 	{
 		$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 		/* @var $aggregator kArray */
 
 		$this->preloadConfigVars(); // preloading will put to cache
 
 		$cache = array_merge(
 			$this->Application->Factory->getToCache(),
 			$this->Application->UnitConfigReader->getToCache(),
 			$this->Application->EventManager->getToCache(),
 			$aggregator->getToCache(),
 			$this->getToCache(),
 			$this->Application->getToCache()
 		);
 
 		$cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s');
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$this->Application->setCache('master:configs_parsed', serialize($cache));
 			$this->Application->setCache('master:last_cache_rebuild', $cache_rebuild_by);
 		}
 		else {
 			$this->Application->setDBCache('configs_parsed', serialize($cache));
 			$this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by);
 		}
 	}
 
 	public function delayUnitProcessing($method, $params)
 	{
 		if ($this->Application->InitDone) {
 			// init already done -> call immediately (happens during installation)
 			$function = Array (&$this->Application, $method);
 			call_user_func_array($function, $params);
 
 			return ;
 		}
 
 		$this->temporaryCache[$method][] = $params;
 	}
 
 	public function applyDelayedUnitProcessing()
 	{
 		foreach ($this->temporaryCache as $method => $method_calls) {
 			$function = Array (&$this->Application, $method);
 
 			foreach ($method_calls as $method_call) {
 				call_user_func_array($function, $method_call);
 			}
 
 			$this->temporaryCache[$method] = Array ();
 		}
 	}
 
 	/**
-	 * Deletes all data, that was cached during unit config parsing (including unit config locations)
+	 * Deletes all data, that was cached during unit config parsing (excluding unit config locations)
 	 *
-	 * @param bool $include_sections
+	 * @param Array $config_variables
 	 * @access public
 	 */
-	public function DeleteUnitCache($include_sections = false)
+	public function DeleteUnitCache($config_variables = null)
 	{
-		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
+		if ( isset($config_variables) && !array_intersect(array_keys($this->originalConfigVariables), $config_variables) ) {
+			// prevent cache reset, when given config variables are not in unit cache
+			return;
+		}
+
+		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 			$this->Application->rebuildCache('master:configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime);
 		}
 		else {
 			$this->Application->rebuildDBCache('configs_parsed', kCache::REBUILD_LATER, CacheSettings::$unitCacheRebuildTime);
 		}
+	}
 
-		if ($include_sections) {
-			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-				$this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
-			}
-			else {
-				$this->Application->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
-			}
+	/**
+	 * Deletes cached section tree, used during permission checking and admin console tree display
+	 *
+	 * @return void
+	 * @access public
+	 */
+	public function DeleteSectionCache()
+	{
+		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
+			$this->Application->rebuildCache('master:sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
+		}
+		else {
+			$this->Application->rebuildDBCache('sections_parsed', kCache::REBUILD_LATER, CacheSettings::$sectionsParsedRebuildTime);
 		}
 	}
 
 	/**
-	 * Preloads widely used configuration variables, so they will get to cache for sure
+	 * Preloads 21 widely used configuration variables, so they will get to cache for sure
 	 *
 	 * @access protected
 	 */
 	protected function preloadConfigVars()
 	{
 		$config_vars = Array (
 			// session related
-			'SessionTimeout',
-			'SessionCookieName',
-			'SessionCookieDomains',
-			'SessionBrowserSignatureCheck',
-			'SessionIPAddressCheck',
-			'CookieSessions',
-			'KeepSessionOnBrowserClose',
-			'User_GuestGroup',
-			'User_LoggedInGroup',
-			'RegistrationUsernameRequired',
+			'SessionTimeout', 'SessionCookieName', 'SessionCookieDomains', 'SessionBrowserSignatureCheck',
+			'SessionIPAddressCheck', 'CookieSessions', 'KeepSessionOnBrowserClose', 'User_GuestGroup',
+			'User_LoggedInGroup', 'RegistrationUsernameRequired',
 
 			// output related
-			'UseModRewrite',
-			'UseContentLanguageNegotiation',
-			'UseOutputCompression',
-			'OutputCompressionLevel',
-			'Config_Site_Time',
-			'SystemTagCache',
+			'UseModRewrite', 'UseContentLanguageNegotiation', 'UseOutputCompression', 'OutputCompressionLevel',
+			'Config_Site_Time', 'SystemTagCache',
 
 			// tracking related
-			'UseChangeLog',
-			'UseVisitorTracking',
-			'ModRewriteUrlEnding',
-			'ForceModRewriteUrlEnding',
+			'UseChangeLog', 'UseVisitorTracking', 'ModRewriteUrlEnding', 'ForceModRewriteUrlEnding',
 			'UseCronForRegularEvent',
 		);
 
 		$escaped_config_vars = array_map(Array (&$this->Conn, 'qstr'), $config_vars);
 
 		$sql = 'SELECT VariableId, VariableName, VariableValue
 				FROM ' . TABLE_PREFIX . 'ConfigurationValues
 				WHERE VariableName IN (' . implode(',', $escaped_config_vars) . ')';
 		$data = $this->Conn->Query($sql, 'VariableId');
 
 		foreach ($data as $variable_id => $variable_info) {
 			$this->configIDs[] = $variable_id;
 			$this->configVariables[ $variable_info['VariableName'] ] = $variable_info['VariableValue'];
 		}
 	}
 
 	/**
 	 * Sets data from cache to object
 	 *
+	 * Used for cases, when ConfigValue is called before LoadApplicationCache method (e.g. session init, url engine init)
+	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
-		$this->configVariables = $data['Application.ConfigHash'];
+		$this->configVariables = $this->originalConfigVariables = $data['Application.ConfigHash'];
 		$this->configIDs = $this->originalConfigIDs = $data['Application.ConfigCacheIds'];
 	}
 
 	/**
 	 * Gets object data for caching
 	 * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
 	 *
 	 * @access public
 	 * @return Array
 	 */
 	public function getToCache()
 	{
 		return Array (
 			'Application.ConfigHash' => $this->configVariables,
 			'Application.ConfigCacheIds' => $this->configIDs,
 
 			// not in use, since it only represents template specific values, not global ones
 			// 'Application.Caches.ConfigVariables' => $this->originalConfigIDs,
 		);
 	}
 
 	/**
 	 * Returns caching type (none, memory, temporary)
 	 *
 	 * @param int $caching_type
 	 * @return bool
 	 * @access public
 	 */
 	public function isCachingType($caching_type)
 	{
 		return $this->cacheHandler->getCachingType() == $caching_type;
 	}
 
 	/**
 	 * Prints caching statistics
 	 *
 	 * @access public
 	 */
 	public function printStatistics()
 	{
 		$this->cacheHandler->printStatistics();
 	}
 
 	/**
 	 * Returns cached $key value from cache named $cache_name
 	 *
 	 * @param int $key key name from cache
 	 * @param bool $store_locally store data locally after retrieved
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
 	{
 		return $this->cacheHandler->getCache($key, $store_locally, $max_rebuild_seconds);
 	}
 
 	/**
 	 * Adds new value to cache $cache_name and identified by key $key
 	 *
 	 * @param int $key key name to add to cache
 	 * @param mixed $value value of chached record
 	 * @param int $expiration when value expires (0 - doesn't expire)
 	 * @return bool
 	 * @access public
 	 */
 	public function setCache($key, $value, $expiration = 0)
 	{
 		return $this->cacheHandler->setCache($key, $value, $expiration);
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 */
 	public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		$this->cacheHandler->rebuildCache($name, $mode, $max_rebuilding_time);
 	}
 
 	/**
 	 * Deletes key from cache
 	 *
 	 * @param string $key
 	 * @access public
 	 */
 	public function deleteCache($key)
 	{
 		$this->cacheHandler->delete($key);
 	}
 
 	/**
 	 * Reset's all memory cache at once
 	 *
 	 * @access public
 	 */
 	public function resetCache()
 	{
 		$this->cacheHandler->reset();
 	}
 
 	/**
 	 * Returns value from database cache
 	 *
 	 * @param string $name key name
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getDBCache($name, $max_rebuild_seconds = 0)
 	{
 		if ( $this->_getDBCache($name . '_rebuild') ) {
 			// cache rebuild requested -> rebuild now
 			$this->deleteDBCache($name . '_rebuild');
 
 			return false;
 		}
 
 		// no serials in cache key OR cache is outdated
 		$wait_seconds = $max_rebuild_seconds;
 
 		while (true) {
 			$cache = $this->_getDBCache($name);
 			$rebuilding = $this->_getDBCache($name . '_rebuilding');
 
 			if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) {
 				// cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready
 				return false;
 			}
 			elseif ( $cache !== false ) {
 				// cache present -> return it
 				return $cache;
 			}
 
 			$wait_seconds -= kCache::WAIT_STEP;
 			sleep(kCache::WAIT_STEP);
 		}
 
 		return false;
 	}
 
 	/**
 	 * Returns value from database cache
 	 *
 	 * @param string $name key name
 	 * @return mixed
 	 * @access protected
 	 */
 	protected function _getDBCache($name)
 	{
 		$this->Conn->nextQueryCachable = true;
 
 		$sql = 'SELECT Data, Cached, LifeTime
 				FROM ' . TABLE_PREFIX . 'Cache
 				WHERE VarName = ' . $this->Conn->qstr($name);
 		$data = $this->Conn->GetRow($sql);
 
 		if ($data) {
 			$lifetime = (int)$data['LifeTime']; // in seconds
 			if (($lifetime > 0) && ($data['Cached'] + $lifetime < adodb_mktime())) {
 				// delete expired
 				$this->Conn->nextQueryCachable = true;
 
 				$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Cache
 						WHERE VarName = ' . $this->Conn->qstr($name);
 				$this->Conn->Query($sql);
 
 				return false;
 			}
 
 			return $data['Data'];
 		}
 
 		return false;
 	}
 
 	/**
 	 * Sets value to database cache
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param int|bool $expiration
 	 * @access public
 	 */
 	public function setDBCache($name, $value, $expiration = false)
 	{
 		$this->deleteDBCache($name . '_rebuilding');
 		$this->_setDBCache($name, $value, $expiration);
 	}
 
 	/**
 	 * Sets value to database cache
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param int|bool $expiration
 	 * @access protected
 	 */
 	protected function _setDBCache($name, $value, $expiration = false)
 	{
 		if ((int)$expiration <= 0) {
 			$expiration = -1;
 		}
 
 		$fields_hash = Array (
 			'VarName' => $name,
 			'Data' => &$value,
 			'Cached' => adodb_mktime(),
 			'LifeTime' => (int)$expiration,
 		);
 
 		$this->Conn->nextQueryCachable = true;
 		$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'Cache', 'REPLACE');
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 */
 	public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		if ( !isset($mode) || $mode == kCache::REBUILD_NOW ) {
 			$this->_setDBCache($name . '_rebuilding', 1, $max_rebuilding_time);
 			$this->deleteDBCache($name . '_rebuild');
 		}
 		elseif ( $mode == kCache::REBUILD_LATER ) {
 			$this->_setDBCache($name . '_rebuild', 1, 0);
 			$this->deleteDBCache($name . '_rebuilding');
 		}
 	}
 
 	/**
 	 * Deletes key from database cache
 	 *
 	 * @param string $name
 	 * @access public
 	 */
 	public function deleteDBCache($name)
 	{
 		$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Cache
 				WHERE VarName = ' . $this->Conn->qstr($name);
 		$this->Conn->Query($sql);
 	}
 
 	/**
 	 * Increments serial based on prefix and it's ID (optional)
 	 *
 	 * @param string $prefix
 	 * @param int $id ID (value of IDField) or ForeignKeyField:ID
 	 * @param bool $increment
 	 * @access public
 	 */
 	public function incrementCacheSerial($prefix, $id = null, $increment = true)
 	{
 		$pascal_case_prefix = implode('', array_map('ucfirst', explode('-', $prefix)));
 		$serial_name = $pascal_case_prefix . (isset($id) ? 'IDSerial:' . $id : 'Serial');
 
 		if ($increment) {
 			if (defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode()) {
 				$this->Application->Debugger->appendHTML('Incrementing serial: <strong>' . $serial_name . '</strong>.');
 			}
 
 			$this->setCache($serial_name, (int)$this->getCache($serial_name) + 1);
 
 			if (!defined('IS_INSTALL') || !IS_INSTALL) {
 				// delete cached mod-rewrite urls related to given prefix and id
 				$delete_clause = isset($id) ? $prefix . ':' . $id : $prefix;
 
 				$sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls
 						WHERE Prefixes LIKE ' . $this->Conn->qstr('%|' . $delete_clause . '|%');
 				$this->Conn->Query($sql);
 			}
 		}
 
 		return $serial_name;
 	}
 
 	/**
 	 * Returns cached category informaton by given cache name. All given category
 	 * information is recached, when at least one of 4 caches is missing.
 	 *
 	 * @param int $category_id
 	 * @param string $name cache name = {filenames, category_designs, category_tree}
 	 * @return string
 	 * @access public
 	 */
 	public function getCategoryCache($category_id, $name)
 	{
 		$serial_name = '[%CIDSerial:' . $category_id . '%]';
 		$cache_key = $name . $serial_name;
 		$ret = $this->getCache($cache_key);
 
 		if ($ret === false) {
 			if (!$category_id) {
 				// don't query database for "Home" category (ID = 0), because it doesn't exist in database
 				return false;
 			}
 
 			// this allows to save 2 sql queries for each category
 			$this->Conn->nextQueryCachable = true;
 			$sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
 					FROM ' . TABLE_PREFIX . 'Category
 					WHERE CategoryId = ' . (int)$category_id;
 			$category_data = $this->Conn->GetRow($sql);
 
 			if ($category_data !== false) {
 				// only direct links to category pages work (symlinks, container pages and so on won't work)
 				$this->setCache('filenames' . $serial_name,				$category_data['NamedParentPath']);
 				$this->setCache('category_designs' . $serial_name,		ltrim($category_data['CachedTemplate'], '/'));
 				$this->setCache('category_tree' . $serial_name,			$category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
 			}
 		}
 
 		return $this->getCache($cache_key);
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/rewrite_url_processor.php
===================================================================
--- branches/5.2.x/core/kernel/managers/rewrite_url_processor.php	(revision 14786)
+++ branches/5.2.x/core/kernel/managers/rewrite_url_processor.php	(revision 14787)
@@ -1,965 +1,976 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 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 kRewriteUrlProcessor extends kUrlProcessor {
 
 	/**
 	 * Holds a reference to httpquery
 	 *
 	 * @var kHttpQuery
 	 * @access protected
 	 */
 	protected $HTTPQuery = null;
 
 	/**
 	 * Urls parts, that needs to be matched by rewrite listeners
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_partsToParse = Array ();
 
 	/**
 	 * Category item prefix, that was found
 	 *
 	 * @var string|bool
 	 * @access public
 	 */
 	public $modulePrefix = false;
 
 	/**
 	 * Template aliases for current theme
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $_templateAliases = null;
 
 	/**
 	 * Domain-based primary language id
 	 *
 	 * @var int
 	 * @access public
 	 */
 	public $primaryLanguageId = false;
 
 	/**
 	 * Domain-based primary theme id
 	 *
 	 * @var int
 	 * @access public
 	 */
 	public $primaryThemeId = false;
 
 	/**
 	 * Possible url endings from ModRewriteUrlEnding configuration variable
 	 *
 	 * @var Array
+	 * @access protected
 	 */
 	protected $_urlEndings = Array ('.html', '/', '');
 
 	/**
+	 * Factory storage sub-set, containing mod-rewrite listeners, used during url building and parsing
+	 *
+	 * @var Array
+	 * @access protected
+	 */
+	protected $rewriteListeners = Array ();
+
+	/**
 	 * Constructor of kRewriteUrlProcessor class
 	 *
 	 * @param $manager
 	 * @return kRewriteUrlProcessor
 	 */
 	public function __construct(&$manager)
 	{
 		parent::__construct($manager);
 
 		$this->HTTPQuery =& $this->Application->recallObject('HTTPQuery');
 
 		// domain based primary language
 		$this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId');
 
 		if (!$this->primaryLanguageId) {
 			// when domain-based language not found -> use site-wide language
 			$this->primaryLanguageId = $this->Application->GetDefaultLanguageId();
 		}
 
 		// domain based primary theme
 		$this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId');
 
 		if (!$this->primaryThemeId) {
 			// when domain-based theme not found -> use site-wide theme
 			$this->primaryThemeId = $this->Application->GetDefaultThemeId(true);
 		}
 
 		$this->_initRewriteListeners();
 	}
 
 	/**
 	 * Parses url
 	 *
 	 * @return void
 	 */
 	public function parseRewriteURL()
 	{
 		$url = $this->Application->GetVar('_mod_rw_url_');
 
 		if ($url) {
 			foreach ($this->_urlEndings as $url_ending) {
 				if (substr($url, strlen($url) - strlen($url_ending)) == $url_ending) {
 					$url = substr($url, 0, strlen($url) - strlen($url_ending));
 					$default_ending = $this->Application->ConfigValue('ModRewriteUrlEnding');
 
 					// user manually typed url with different url ending -> redirect to same url with default url ending
 					if (($url_ending != $default_ending) && $this->Application->ConfigValue('ForceModRewriteUrlEnding')) {
 						$target_url = $this->Application->BaseURL() . $url . $default_ending;
 						$this->Application->Redirect('external:' . $target_url, Array ('response_code' => 301));
 					}
 
 					break;
 				}
 			}
 		}
 
 		$cached = $this->_getCachedUrl($url);
 
 		if ( $cached !== false ) {
 			$vars = $cached['vars'];
 			$passed = $cached['passed'];
 		}
 		else {
 			$vars = $this->parse($url);
 			$passed = $vars['pass']; // also used in bottom of this method
 			unset($vars['pass']);
 
 			if ( !$this->_partsToParse ) {
 				// don't cache 404 Not Found
 				$this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed));
 			}
 
 			if ( $this->Application->GetVarDirect('t', 'Post') ) {
 				// template from POST overrides template from URL.
 				$vars['t'] = $this->Application->GetVarDirect('t', 'Post');
 
 				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);
 		}
 
 		$this->_initAll(); // also will use parsed language to load phrases from it
 
 		$this->HTTPQuery->finalizeParsing($passed);
 	}
 
 	/**
 	 * Returns url parsing result from cache or false, when not yet parsed
 	 *
 	 * @param $url
 	 * @return Array|bool
 	 * @access protected
 	 */
 	protected function _getCachedUrl($url)
 	{
 		if (!$url) {
 			return false;
 		}
 
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'CachedUrls
 				WHERE Hash = ' . crc32($url) . ' AND DomainId = ' . (int)$this->Application->siteDomainField('DomainId');
 		$data = $this->Conn->GetRow($sql);
 
 		if ($data) {
 			$lifetime = (int)$data['LifeTime']; // in seconds
 			if (($lifetime > 0) && ($data['Cached'] + $lifetime < adodb_mktime())) {
 				// delete expired
 				$sql = 'DELETE FROM ' . TABLE_PREFIX . 'CachedUrls
 						WHERE UrlId = ' . $data['UrlId'];
 				$this->Conn->Query($sql);
 
 				return false;
 			}
 
 			return unserialize($data['ParsedVars']);
 		}
 
 		return false;
 	}
 
 	/**
 	 * Caches url
 	 *
 	 * @param string $url
 	 * @param Array $data
 	 * @return void
 	 * @access protected
 	 */
 	protected function _setCachedUrl($url, $data)
 	{
 		if (!$url) {
 			return ;
 		}
 
 		$vars = $data['vars'];
 		$passed = $data['passed'];
 		sort($passed);
 
 		// get expiration
 		if ($vars['m_cat_id'] > 0) {
 			$sql = 'SELECT PageExpiration
 					FROM ' . TABLE_PREFIX . 'Category
 					WHERE CategoryId = ' . $vars['m_cat_id'];
 			$expiration = $this->Conn->GetOne($sql);
 		}
 
 		// get prefixes
 		$prefixes = Array ();
 		$m_index = array_search('m', $passed);
 
 		if ($m_index !== false) {
 			unset($passed[$m_index]);
 
 			if ($vars['m_cat_id'] > 0) {
 				$prefixes[] = 'c:' . $vars['m_cat_id'];
 			}
 
 			$prefixes[] = 'lang:' . $vars['m_lang'];
 			$prefixes[] = 'theme:' . $vars['m_theme'];
 		}
 
 		foreach ($passed as $prefix) {
 			if (array_key_exists($prefix . '_id', $vars) && is_numeric($vars[$prefix . '_id'])) {
 				$prefixes[] = $prefix . ':' . $vars[$prefix . '_id'];
 			}
 			else {
 				$prefixes[] = $prefix;
 			}
 		}
 
 		$fields_hash = Array (
 			'Url' => $url,
 			'Hash' => crc32($url),
 			'DomainId' => (int)$this->Application->siteDomainField('DomainId'),
 			'Prefixes' => $prefixes ? '|' . implode('|', $prefixes) . '|' : '',
 			'ParsedVars' => serialize($data),
 			'Cached' => adodb_mktime(),
 			'LifeTime' => isset($expiration) && is_numeric($expiration) ? $expiration : -1
 		);
 
 		$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'CachedUrls');
 	}
 
 	/**
 	 * Loads all registered rewrite listeners, so they could be quickly accessed later
 	 *
 	 * @access protected
 	 */
 	protected function _initRewriteListeners()
 	{
 		static $init_done = false;
 
 		if ($init_done || count($this->Application->RewriteListeners) == 0) {
 			// not initialized OR mod-rewrite url with missing config cache
 			return ;
 		}
 
 		foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
 			foreach ($listener_data['listener'] as $index => $rewrite_listener) {
 				list ($listener_prefix, $listener_method) = explode(':', $rewrite_listener);
-				$listener =& $this->Application->recallObject($listener_prefix);
 
-				$this->Application->RewriteListeners[$prefix][$index] = Array (&$listener, $listener_method);
+				// don't use temp variable, since it will swap objects in Factory in PHP5
+				$this->rewriteListeners[$prefix][$index] = Array ();
+				$this->rewriteListeners[$prefix][$index][0] =& $this->Application->recallObject($listener_prefix);
+				$this->rewriteListeners[$prefix][$index][1] = $listener_method;
 			}
 		}
 
 		define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding'));
 
 		$init_done = true;
 	}
 
 	/**
 	 * Parses given string into a set of variables (url in this case)
 	 *
 	 * @param string $string
 	 * @param string $pass_name
 	 * @return Array
 	 * @access public
 	 */
 	public function parse($string, $pass_name = 'pass')
 	{
 		$vars = Array ($pass_name => Array ('m'));
 		$url_parts = $string ? explode('/', trim(mb_strtolower($string, 'UTF-8'), '/')) : Array ();
 
 		$this->_partsToParse = $url_parts;
 
 		if ( ($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts ) {
 			$this->_setDefaultValues($vars);
 		}
 
 		if ( !$url_parts ) {
 			$this->_initAll();
 			$vars['t'] = $this->Application->UrlManager->getTemplateName();
 
 			return $vars;
 		}
 
 		$this->_parseLanguage($url_parts, $vars);
 		$this->_parseTheme($url_parts, $vars);
 
 		// 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;
 		}
 
 		$this->_parsePhysicalTemplate($url_parts, $vars);
 
 		if ( ($this->modulePrefix === false) && $vars['m_cat_id'] && !$this->_partsToParse ) {
 			// no category item found, but category found and all url matched -> module index page
 
 			return $vars;
 		}
 
 		if ( $this->_partsToParse ) {
 			$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;
 	}
 
 	/**
 	 * Initializes theme & language based on parse results
 	 *
 	 * @return void
 	 * @access protected
 	 */
 	protected function _initAll()
 	{
 		$this->Application->VerifyThemeId();
 		$this->Application->VerifyLanguageId();
 
 		// no need, since we don't have any cached phrase IDs + nobody will use PhrasesCache::LanguageId soon
 		// $this->Application->Phrases->Init('phrases');
 	}
 
 	/**
 	 * Sets default parsed values before actual url parsing (only, for empty url)
 	 *
 	 * @param Array $vars
 	 * @access protected
 	 */
 	protected function _setDefaultValues(&$vars)
 	{
 		$defaults = Array (
 			'm_cat_id' => 0, // no category
 			'm_cat_page' => 1, // first category page
 			'm_opener' => 's', // stay on same page
 			't' => 'index' // main site page
 		);
 
 		if ($this->primaryLanguageId) {
 			// domain-based primary language
 			$defaults['m_lang'] = $this->primaryLanguageId;
 		}
 
 		if ($this->primaryThemeId) {
 			// domain-based primary theme
 			$defaults['m_theme'] = $this->primaryThemeId;
 		}
 
 		foreach ($defaults as $default_key => $default_value) {
 			if ($this->HTTPQuery->Get($default_key) === false) {
 				$vars[$default_key] = $default_value;
 			}
 		}
 	}
 
 	/**
 	 * Processes url using rewrite listeners
 	 *
 	 * Pattern: Chain of Command
 	 *
 	 * @param Array $url_parts
 	 * @param Array $vars
 	 * @return bool
 	 * @access protected
 	 */
 	protected function _processRewriteListeners(&$url_parts, &$vars)
 	{
 		$this->_initRewriteListeners();
 		$page_number = $this->_parsePage($url_parts, $vars);
 
-		foreach ($this->Application->RewriteListeners as $prefix => $listeners) {
+		foreach ($this->rewriteListeners as $prefix => $listeners) {
 			// 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;
 			}
 
 			// $listeners[1] - listener, used for parsing
 			$listener_result = $listeners[1][0]->$listeners[1][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;
 	}
 
 	/**
 	 * Set's page (when found) to all modules
 	 *
 	 * @param Array $url_parts
 	 * @param Array $vars
 	 * @return string
 	 * @access protected
 	 *
 	 * @todo Should find a way, how to determine what rewrite listerner page is it
 	 */
 	protected function _parsePage(&$url_parts, &$vars)
 	{
 		if (!$url_parts) {
 			return false;
 		}
 
 		$page_number = end($url_parts);
 		if (!is_numeric($page_number)) {
 			return false;
 		}
 
 		array_pop($url_parts);
 		$this->partParsed($page_number, 'rtl');
 
 		return $page_number;
 	}
 
 	/**
 	 * Gets language part from url
 	 *
 	 * @param Array $url_parts
 	 * @param Array $vars
 	 * @return bool
 	 * @access protected
 	 */
 	protected 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);
 
 		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);
 				$this->partParsed($url_part);
 			}
 			elseif ($this->primaryLanguageId) {
 				// use domain-based primary language instead of site-wide primary language
 				$vars['m_lang'] = $this->primaryLanguageId;
 			}
 
 			return true;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Gets theme part from url
 	 *
 	 * @param Array $url_parts
 	 * @param Array $vars
 	 * @return bool
 	 */
 	protected 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, TemplateAliases
 				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['TemplateAliases']) {
 				$this->_templateAliases = unserialize($theme_info['TemplateAliases']);
 			}
 			else {
 				$this->_templateAliases = Array ();
 			}
 
 			if ($theme_info['SortKey'] == 2) {
 				// theme was found by name
 				array_shift($url_parts);
 				$this->partParsed($url_part);
 			}
 			elseif ($this->primaryThemeId) {
 				// use domain-based primary theme instead of site-wide primary theme
 				$vars['m_theme'] = $this->primaryThemeId;
 			}
 
 			return true;
 		}
 
 		$vars['m_theme'] = 0; // required, because used later for category/template detection
 
 		return false;
 	}
 
 	/**
 	 * Parses real template name from url
 	 *
 	 * @param Array $url_parts
 	 * @param Array $vars
 	 * @return bool
 	 */
 	protected function _parsePhysicalTemplate($url_parts, &$vars)
 	{
 		if ( !$url_parts ) {
 			return false;
 		}
 
 		$themes_helper =& $this->Application->recallObject('ThemesHelper');
 		/* @var $themes_helper kThemesHelper */
 
 		do {
 			$template_path = implode('/', $url_parts);
 			$template_found = $themes_helper->getTemplateId($template_path, $vars['m_theme']);
 
 			if ( !$template_found ) {
 				array_shift($url_parts);
 			}
 		} while ( !$template_found && $url_parts );
 
 		if ( $template_found ) {
 			$vars['t'] = $template_path;
 
 			$template_parts = explode('/', $template_path);
 
 			while ( $template_parts ) {
 				$this->partParsed(array_pop($template_parts), 'rtl');
 			}
 
 			// 1. will damage actual category during category item review add process
 			// 2. will use "use_section" parameter of "m_Link" tag to gain same effect
 //			$vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']);
 
 			return true;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Returns environment variable values for given prefix (uses directly given params, when available)
 	 *
 	 * @param string $prefix_special
 	 * @param Array $params
 	 * @param bool $keep_events
 	 * @return Array
 	 * @access public
 	 */
 	public function getProcessedParams($prefix_special, &$params, $keep_events)
 	{
 		list ($prefix) = explode('.', $prefix_special);
 
 		$query_vars = $this->Application->getUnitOption($prefix, 'QueryString', Array ());
 		/* @var $query_vars Array */
 
 		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 $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
 	 * @access public
 	 * @todo Move to kPlainUrlProcessor
 	 */
 	public function GetItemTemplate($category, $module_prefix)
 	{
 		$category_id = is_array($category) ? $category['CategoryId'] : $category;
 		$cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CIDSerial:' . $category_id . '%]:' . $module_prefix;
 
 		$cached_value = $this->Application->getCache($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
 		$primary_lang = $this->Application->GetDefaultLanguageId();
 		$item_template_field_id = $this->getItemTemplateCustomField($module_prefix);
 
 		// looking for item template through cats hierarchy sorted by parent path
 		$query = '	SELECT ccd.l' . $primary_lang . '_cust_' . $item_template_field_id . ',
 								FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') AS Ord1,
 								c.CategoryId, c.Name, ccd.l' . $primary_lang . '_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.l' . $primary_lang . '_cust_' . $item_template_field_id . ' != \'\'
 					ORDER BY FIND_IN_SET(c.CategoryId, ' . $this->Conn->qstr($parent_path) . ') DESC';
 		$item_template = $this->Conn->GetOne($query);
 
 		if ( !isset($this->_templateAliases) ) {
 			// when empty url OR mod-rewrite disabled
 
 			$themes_helper =& $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			$sql = 'SELECT TemplateAliases
 					FROM ' . TABLE_PREFIX . 'Theme
 					WHERE ThemeId = ' . (int)$themes_helper->getCurrentThemeId();
 			$template_aliases = $this->Conn->GetOne($sql);
 
 			$this->_templateAliases = $template_aliases ? unserialize($template_aliases) : Array ();
 		}
 
 		if ( substr($item_template, 0, 1) == '#' ) {
 			// it's template alias + "#" isn't allowed in filenames
 			$item_template = (string)getArrayValue($this->_templateAliases, $item_template);
 		}
 
 		$this->Application->setCache($cache_key, $item_template);
 
 		return $item_template;
 	}
 
 	/**
 	 * Returns category custom field id, where given module prefix item template name is stored
 	 *
 	 * @param string $module_prefix
 	 * @return int
 	 * @access public
 	 * @todo Move to kPlainUrlProcessor; decrease visibility, since used only during upgrade
 	 */
 	public function getItemTemplateCustomField($module_prefix)
 	{
 		$cache_key = __CLASS__ . '::' . __FUNCTION__ . '[%CfSerial%]:' . $module_prefix;
 		$cached_value = $this->Application->getCache($cache_key);
 
 		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($cache_key, $item_template_field_id);
 
 		return $item_template_field_id;
 	}
 
 	/**
 	 * Marks url part as parsed
 	 *
 	 * @param string $url_part
 	 * @param string $parse_direction
 	 * @access public
 	 */
 	public function partParsed($url_part, $parse_direction = 'ltr')
 	{
 		if ( !$this->_partsToParse ) {
 			return ;
 		}
 
 		if ( $parse_direction == 'ltr' ) {
 			$expected_url_part = reset($this->_partsToParse);
 
 			if ( $url_part == $expected_url_part ) {
 				array_shift($this->_partsToParse);
 			}
 		}
 		else {
 			$expected_url_part = end($this->_partsToParse);
 
 			if ( $url_part == $expected_url_part ) {
 				array_pop($this->_partsToParse);
 			}
 		}
 
 		if ( $url_part != $expected_url_part ) {
 			trigger_error('partParsed: expected URL part "<strong>' . $expected_url_part . '</strong>", received URL part "<strong>' . $url_part . '</strong>"', E_USER_NOTICE);
 		}
 	}
 
 	/**
 	 * Builds url
 	 *
 	 * @param string $t
 	 * @param Array $params
 	 * @param string $pass
 	 * @param bool $pass_events
 	 * @param bool $env_var
 	 * @return string
 	 * @access public
 	 */
 	public function build($t, $params, $pass = 'all', $pass_events = false, $env_var = false)
 	{
 		if ( $this->Application->GetVar('admin') || (array_key_exists('admin', $params) && $params['admin']) ) {
 			$params['admin'] = 1;
 
 			if ( !array_key_exists('editing_mode', $params) ) {
 				$params['editing_mode'] = EDITING_MODE;
 			}
 		}
 
 		$ret = '';
 		$env = '';
 
 		$encode = false;
 
 		if ( isset($params['__URLENCODE__']) ) {
 			$encode = $params['__URLENCODE__'];
 			unset($params['__URLENCODE__']);
 		}
 
 		if ( isset($params['__SSL__']) ) {
 			unset($params['__SSL__']);
 		}
 
 		$catalog_item_found = false;
 		$pass_info = $this->getPassInfo($pass);
 
 		if ( $pass_info ) {
 			if ( $pass_info[0] == 'm' ) {
 				array_shift($pass_info);
 			}
 
 			$inject_parts = Array (); // url parts for beginning of url
 			$params['t'] = $t; // make template available for rewrite listeners
 			$params['pass_template'] = true; // by default we keep given template in resulting url
 
 			if ( !array_key_exists('pass_category', $params) ) {
 				$params['pass_category'] = false; // by default we don't keep categories in url
 			}
 
 			foreach ($pass_info as $pass_index => $pass_element) {
 				list ($prefix) = explode('.', $pass_element);
 				$catalog_item = $this->Application->findModule('Var', $prefix) && $this->Application->getUnitOption($prefix, 'CatalogItem');
 
-				if ( array_key_exists($prefix, $this->Application->RewriteListeners) ) {
+				if ( array_key_exists($prefix, $this->rewriteListeners) ) {
 					// if next prefix is same as current, but with special => exclude current prefix from url
 					$next_prefix = array_key_exists($pass_index + 1, $pass_info) ? $pass_info[$pass_index + 1] : false;
 					if ( $next_prefix ) {
 						$next_prefix = substr($next_prefix, 0, strlen($prefix) + 1);
 						if ( $prefix . '.' == $next_prefix ) {
 							continue;
 						}
 					}
 
 					// rewritten url part
 					$url_part = $this->BuildModuleEnv($pass_element, $params, $pass_events);
 
 					if ( is_string($url_part) && $url_part ) {
 						$ret .= $url_part . '/';
 
 						if ( $catalog_item ) {
 							// pass category later only for catalog items
 							$catalog_item_found = true;
 						}
 					}
 					elseif ( is_array($url_part) ) {
 						// rewrite listener want to insert something at the beginning of url too
 						if ( $url_part[0] ) {
 							$inject_parts[] = $url_part[0];
 						}
 
 						if ( $url_part[1] ) {
 							$ret .= $url_part[1] . '/';
 						}
 
 						if ( $catalog_item ) {
 							// pass category later only for catalog items
 							$catalog_item_found = true;
 						}
 					}
 					elseif ( $url_part === false ) {
 						// rewrite listener decided not to rewrite given $pass_element
 						$env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events);
 					}
 				}
 				else {
 					$env .= ':' . $this->manager->plain->BuildModuleEnv($pass_element, $params, $pass_events);
 				}
 			}
 
 			if ( $catalog_item_found || preg_match('/c\.[-\d]*/', implode(',', $pass_info)) ) {
 				// "c" prefix is present -> keep category
 				$params['pass_category'] = true;
 			}
 
 			$params['inject_parts'] = $inject_parts;
 
 			$ret = $this->BuildModuleEnv('m', $params, $pass_events) . '/' . $ret;
 			$cat_processed = array_key_exists('category_processed', $params) && $params['category_processed'];
 
 			// remove temporary parameters used by listeners
 			unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']);
 
 			$ret = trim($ret, '/');
 
 			if ( isset($params['url_ending']) ) {
 				if ( $ret ) {
 					$ret .= $params['url_ending'];
 				}
 
 				unset($params['url_ending']);
 			}
 			elseif ( $ret ) {
 				$ret .= MOD_REWRITE_URL_ENDING;
 			}
 
 			if ( $env ) {
 				$params[ENV_VAR_NAME] = ltrim($env, ':');
 			}
 		}
 
 		unset($params['pass'], $params['opener'], $params['m_event']);
 
 		if ( array_key_exists('escape', $params) && $params['escape'] ) {
 			$ret = addslashes($ret);
 			unset($params['escape']);
 		}
 
 		$ret = str_replace('%2F', '/', urlencode($ret));
 
 		if ( $params ) {
 			$params_str = '';
 			$join_string = $encode ? '&' : '&amp;';
 
 			foreach ($params as $param => $value) {
 				$params_str .= $join_string . $param . '=' . $value;
 			}
 
 			$ret .= '?' . substr($params_str, strlen($join_string));
 		}
 
 		if ( $encode ) {
 			$ret = str_replace('\\', '%5C', $ret);
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Builds env part that corresponds prefix passed
 	 *
 	 * @param string $prefix_special item's prefix & [special]
 	 * @param Array $params url params
 	 * @param bool $pass_events
 	 * @return string
 	 * @access protected
 	 */
 	protected function BuildModuleEnv($prefix_special, &$params, $pass_events = false)
 	{
 		list ($prefix) = explode('.', $prefix_special);
 
 		$url_parts = Array ();
-		$listener = $this->Application->RewriteListeners[$prefix][0];
+		$listener = $this->rewriteListeners[$prefix][0];
 
 		$ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $pass_events);
 
 		return $ret;
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/kernel/utility/unit_config_reader.php
===================================================================
--- branches/5.2.x/core/kernel/utility/unit_config_reader.php	(revision 14786)
+++ branches/5.2.x/core/kernel/utility/unit_config_reader.php	(revision 14787)
@@ -1,1061 +1,1065 @@
 <?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 kUnitConfigReader extends kBase implements kiCacheable {
 
 	/**
 	 * Configs reader
 	 *
 	 * @var Array
 	 * @access private
 	 */
 	var $configData = Array();
 	var $configFiles = Array();
 
 	var $CacheExpired = false;
 
 	var $prefixFiles = array();
 
 	var $ProcessAllConfigs = false;
 	var $FinalStage = false;
 	var $StoreCache = false;
 	var $AfterConfigProcessed = array();
 
 	/**
 	 * Escaped directory separator for using in regular expressions
 	 *
 	 * @var string
 	 */
 	var $_directorySeparator = '';
 
 	/**
 	 * Regular expression for detecting module folder
 	 *
 	 * @var string
 	 */
 	var $_moduleFolderRegExp = '';
 
 	/**
 	 * Folders to skip during unit config search
 	 *
 	 * @var Array
 	 */
 	var $_skipFolders = Array ('CVS', '.svn', 'admin_templates', 'libchart');
 
 	/**
 	 * Creates instance of unit config reader
 	 *
 	 */
 	public function __construct()
 	{
 		parent::__construct();
 
 		$this->_directorySeparator = preg_quote(DIRECTORY_SEPARATOR);
 
 		$editor_path = explode('/', trim(EDITOR_PATH, '/'));
 		$this->_skipFolders[] = array_pop($editor_path); // last of cmseditor folders
 
 		$this->_moduleFolderRegExp = '#' . $this->_directorySeparator . '(core|modules' . $this->_directorySeparator . '.*?)' . $this->_directorySeparator . '#';
 	}
 
 	/**
 	 * Sets data from cache to object
 	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
 		$this->prefixFiles = $data['ConfigReader.prefixFiles'];
 	}
 
 	/**
 	 * Gets object data for caching
 	 *
 	 * @access public
 	 * @return Array
 	 */
 	public function getToCache()
 	{
 		return Array (
 			'ConfigReader.prefixFiles' => $this->prefixFiles,
 		);
 	}
 
 	function scanModules($folderPath, $cache = true)
 	{
 		if (defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE')) {
 			// disable config caching during installation
 			$cache = false;
 		}
 
 		if ($cache) {
 			$restored = $this->Application->cacheManager->LoadUnitCache();
 
 			if ($restored) {
 				if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
 					$this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache');
 				}
 
 				return;
 			}
 		}
 
 		if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
 			$this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache');
 		}
 
 		$this->ProcessAllConfigs = true;
 
 		$this->includeConfigFiles($folderPath, $cache);
 		$this->ParseConfigs();
 
 		// tell AfterConfigRead to store cache if needed
 		// can't store it here because AfterConfigRead needs ability to change config data
 		$this->StoreCache = $cache;
 
-		if (!$this->Application->InitDone) {
+		if ( !$this->Application->InitDone ) {
 			// scanModules is called multiple times during installation process
 			$this->Application->InitManagers();
+
+			// get build-in rewrite listeners ONLY to be able to parse mod-rewrite url when unit config cache is missing
+			$this->retrieveCollections();
+			$this->_sortRewriteListeners();
 		}
 
 		$this->Application->cacheManager->applyDelayedUnitProcessing();
 	}
 
 	function findConfigFiles($folderPath, $level = 0)
 	{
 		// if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted
 		$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
 		$folderPath = preg_replace($reg_exp, '', $folderPath, 1); // this make sense, since $folderPath may NOT contain FULL_PATH
 
 		$base_folder = FULL_PATH . $folderPath . DIRECTORY_SEPARATOR;
 		$sub_folders = glob($base_folder . '*', GLOB_ONLYDIR);
 		if (!$sub_folders) {
 			return ;
 		}
 
 		if ($level == 0) {
 			// don't scan Front-End themes because of extensive directory structure
 			$sub_folders = array_diff($sub_folders, Array ($base_folder . 'themes', $base_folder . 'tools'));
 		}
 
 		foreach ($sub_folders as $full_path) {
 			$sub_folder = substr($full_path, strlen($base_folder));
 
 			if (in_array($sub_folder, $this->_skipFolders)) {
 				continue;
 			}
 
 			if (preg_match('/^\./', $sub_folder)) {
 				// don't scan ".folders"
 				continue;
 			}
 
 			$config_name = $this->getConfigName($folderPath . DIRECTORY_SEPARATOR . $sub_folder);
 
 			if (file_exists(FULL_PATH . $config_name)) {
 				$this->configFiles[] = $config_name;
 			}
 
 			$this->findConfigFiles($full_path, $level + 1);
 		}
 	}
 
 	function includeConfigFiles($folderPath, $cache = true)
 	{
 		$this->Application->refreshModuleInfo();
 
 		if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 			$data = $this->Application->getCache('master:config_files', false, CacheSettings::$unitCacheRebuildTime);
 		}
 		else {
 			$data = $this->Application->getDBCache('config_files', CacheSettings::$unitCacheRebuildTime);
 		}
 
 		if ( $data ) {
 			$this->configFiles = unserialize($data);
 
 			if ( !defined('DBG_VALIDATE_CONFIGS') && !DBG_VALIDATE_CONFIGS ) {
 				shuffle($this->configFiles);
 			}
 		}
 		else {
 			if ( $cache ) {
 				if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 					$this->Application->rebuildCache('master:config_files', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
 				}
 				else {
 					$this->Application->rebuildDBCache('config_files', kCache::REBUILD_NOW, CacheSettings::$unitCacheRebuildTime);
 				}
 			}
 
 			$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from core directory
 			$this->findConfigFiles($folderPath); // search from modules directory
 
 			if ( $cache ) {
 				if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
 					$this->Application->setCache('master:config_files', serialize($this->configFiles));
 				}
 				else {
 					$this->Application->setDBCache('config_files', serialize($this->configFiles));
 				}
 			}
 		}
 
 		foreach ($this->configFiles as $filename) {
 			$prefix = $this->PreloadConfigFile($filename);
 
 			if (!$prefix) {
 				throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>');
 			}
 		}
 
 		if ($cache) {
 			unset($this->configFiles);
 		}
 	}
 
 	/**
 	 * Process all read config files - called ONLY when there is no cache!
 	 *
 	 */
 	function ParseConfigs()
 	{
 		// 1. process normal configs and their dependencies
 		$prioritized_configs = array();
 		foreach ($this->configData as $prefix => $config) {
 			if (isset($config['ConfigPriority'])) {
 				$prioritized_configs[$prefix] = $config['ConfigPriority'];
 				continue;
 			}
 			$this->parseConfig($prefix);
 		}
 
 		foreach ($this->configData as $prefix => $config) {
 			$this->ProcessDependencies($prefix);
 			$this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 			$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
 		}
 
 		// 2. process prioritized configs and their dependencies
 		asort($prioritized_configs);
 		foreach ($prioritized_configs as $prefix => $priority) {
 			$this->parseConfig($prefix);
 		}
 
 		foreach ($prioritized_configs as $prefix => $priority) {
 			$this->ProcessDependencies($prefix);
 		}
 	}
 
 	function AfterConfigRead($store_cache = null)
 	{
 //		if (!$this->ProcessAllConfigs) return ;
 		$this->FinalStage = true;
 		foreach ($this->configData as $prefix => $config) {
 			$this->runAfterConfigRead($prefix);
 		}
 
 		if ( !isset($store_cache) ) {
 			// $store_cache not overridden -> use global setting
 			$store_cache = $this->StoreCache;
 		}
 
 		if ($store_cache || (defined('IS_INSTALL') && IS_INSTALL)) {
 			// cache is not stored during install, but dynamic clones should be processed in any case
 			$this->processDynamicClones();
 			$this->retrieveCollections();
 		}
 
 		if ($store_cache) {
 			$this->_sortRewriteListeners();
 
 			$after_event = new kEvent('adm:OnAfterCacheRebuild');
 			$this->Application->HandleEvent($after_event);
 
 			$this->Application->cacheManager->UpdateUnitCache();
 
 			if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) {
 				// validate configs here to have changes from OnAfterConfigRead hooks to prefixes
 				foreach ($this->configData as $prefix => $config) {
 					if (!isset($config['TableName'])) continue;
 					$this->ValidateConfig($prefix);
 				}
 			}
 		}
 	}
 
 	/**
 	 * Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first)
 	 *
 	 */
 	function _sortRewriteListeners()
 	{
 		$listeners = Array ();
 		$prioritized_listeners = Array ();
 
 		// process non-prioritized listeners
 		foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
 			if ($listener_data['priority'] === false) {
 				$listeners[$prefix] = $listener_data;
 			}
 			else {
 				$prioritized_listeners[$prefix] = $listener_data['priority'];
 			}
 		}
 
 		// process prioritized listeners
 		asort($prioritized_listeners, SORT_NUMERIC);
 		foreach ($prioritized_listeners as $prefix => $priority) {
 			$listeners[$prefix] = $this->Application->RewriteListeners[$prefix];
 		}
 
 		$this->Application->RewriteListeners = $listeners;
 	}
 
 	/**
 	 * Re-reads all configs
 	 *
 	 */
 	function ReReadConfigs()
 	{
 		$this->Application->cacheManager->EmptyUnitCache();
 
 		// parse all configs
 		$this->ProcessAllConfigs = true;
 		$this->AfterConfigProcessed = Array ();
 		$this->includeConfigFiles(MODULES_PATH, false);
 		$this->ParseConfigs();
 		$this->AfterConfigRead(false);
 		$this->processDynamicClones();
 
 		// don't call kUnitConfigReader::retrieveCollections since it
 		// will overwrite what we already have in kApplication class instance
 	}
 
 	/**
 	 * Process clones, that were defined via OnAfterConfigRead event
 	 *
 	 */
 	function processDynamicClones()
 	{
 		$new_clones = Array();
 		foreach ($this->configData as $prefix => $config) {
 			$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
 
 			if ($clones) {
 				$new_clones = array_merge($new_clones, $clones);
 			}
 		}
 
 		// execute delayed methods for cloned unit configs
 		$this->Application->cacheManager->applyDelayedUnitProcessing();
 
 		// call OnAfterConfigRead for cloned configs
 		$new_clones = array_unique($new_clones);
 		foreach ($new_clones as $prefix) {
 			$this->runAfterConfigRead($prefix);
 		}
 	}
 
 	/**
-	 * Process all collectable unit config options here to also catch ones, defined from OnAfterConfigRead events
+	 * Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events
 	 *
 	 */
 	function retrieveCollections()
 	{
 		foreach ($this->configData as $prefix => $config) {
 			// collect replacement templates
 			if (array_key_exists('ReplacementTemplates', $config) && $config['ReplacementTemplates']) {
 				$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config['ReplacementTemplates']);
 			}
 
 			// collect rewrite listeners
 			if (array_key_exists('RewriteListener', $config) && $config['RewriteListener']) {
 				$rewrite_listeners = $config['RewriteListener'];
 
 				if (!is_array($rewrite_listeners)) {
 					// when one method is used to build and parse url
 					$rewrite_listeners = Array ($rewrite_listeners, $rewrite_listeners);
 				}
 
 				foreach ($rewrite_listeners as $index => $rewrite_listener) {
 					if (strpos($rewrite_listener, ':') === false) {
 						$rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener;
 					}
 				}
 
 				$rewrite_priority = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false;
 
 				$this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listeners, 'priority' => $rewrite_priority);
 			}
 		}
 	}
 
 	/**
 	 * Register nessasary classes
 	 * This method should only process the data which is cached!
 	 *
 	 * @param string $prefix
 	 * @access private
 	 */
 	function parseConfig($prefix)
 	{
 		$this->parseClasses($prefix);
 		$this->parseAgents($prefix);
 		$this->parseHooks($prefix);
 		$this->parseAggregatedTags($prefix);
 	}
 
 	protected function parseClasses($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$register_classes = $this->getClasses($prefix);
 
 		foreach ($register_classes as $class_info) {
 			// remember class dependencies
 			$class_name = $class_info['class'];
 			$require_classes = isset($class_info['require_classes']) ? $class_info['require_classes'] : Array ();
 
 			if ($require_classes) {
 				$require_classes = (array)$require_classes;
 
 				if ( !isset($config['_Dependencies'][$class_name]) ) {
 					$config['_Dependencies'][$class_name] = Array ();
 				}
 
 				$config['_Dependencies'][$class_name] = array_merge($config['_Dependencies'][$class_name], $require_classes);
 			}
 
 			// register class
 			$this->Application->registerClass(
 				$class_name,
 				$config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'],
 				$class_info['pseudo']
 			);
 
 			if ( isset($class_info['build_event']) && $class_info['build_event'] ) {
 				$this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event']));
 			}
 		}
 	}
 
 	protected function parseAgents($prefix)
 	{
 		$config =& $this->configData[$prefix];
 
 		if ( !isset($config['RegularEvents']) || !$config['RegularEvents'] ) {
 			return ;
 		}
 
 		$regular_events = $config['RegularEvents'];
 
 		foreach ($regular_events as $short_name => $regular_event_info) {
 			$event_status = array_key_exists('Status', $regular_event_info) ? $regular_event_info['Status'] : STATUS_ACTIVE;
 			$this->Application->delayUnitProcessing('registerAgent', Array ( $short_name, $config['Prefix'] . ':' . $regular_event_info['EventName'], $regular_event_info['RunInterval'], $regular_event_info['Type'], $event_status ));
 		}
 	}
 
 	protected function parseHooks($prefix)
 	{
 		$config =& $this->configData[$prefix];
 
 		if ( !isset($config['Hooks']) || !$config['Hooks'] ) {
 			return ;
 		}
 
 		$hooks = $config['Hooks'];
 
 		foreach ($hooks as $hook) {
 			if ( isset($config['ParentPrefix']) && ($hook['HookToPrefix'] == $config['ParentPrefix']) ) {
 				trigger_error('Depricated Hook Usage [prefix: <strong>' . $config['Prefix'] . '</strong>; do_prefix: <strong>' . $hook['DoPrefix'] . '</strong>] use <strong>#PARENT#</strong> as <strong>HookToPrefix</strong> value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
 			}
 
 			if ($hook['HookToPrefix'] == '') {
 				// new: set hooktoprefix to current prefix if not set
 				$hook['HookToPrefix'] = $config['Prefix'];
 			}
 
 			if ( isset($config['ParentPrefix']) ) {
 				// new: allow to set hook to parent prefix what ever it is
 				if ($hook['HookToPrefix'] == '#PARENT#') {
 					$hook['HookToPrefix'] = $config['ParentPrefix'];
 				}
 
 				if ($hook['DoPrefix'] == '#PARENT#') {
 					$hook['DoPrefix'] = $config['ParentPrefix'];
 				}
 			}
 			elseif ($hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#') {
 				// we need parent prefix but it's not set !
 				continue;
 			}
 
 			$hook_events = (array)$hook['HookToEvent'];
 			$do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix'];
 
 			foreach ($hook_events as $hook_event) {
 				$hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event;
 				$do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'];
 
 				$this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional']));
 			}
 		}
 	}
 
 	protected function parseAggregatedTags($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$aggregated_tags = isset($config['AggregateTags']) ? $config['AggregateTags'] : Array ();
 
 		foreach ($aggregated_tags as $aggregate_tag) {
 			if ( isset($config['ParentPrefix']) ) {
 				if ($aggregate_tag['AggregateTo'] == $config['ParentPrefix']) {
 					trigger_error('Depricated Aggregate Tag Usage [prefix: <b>'.$config['Prefix'].'</b>; AggregateTo: <b>'.$aggregate_tag['AggregateTo'].'</b>] use <b>#PARENT#</b> as <b>AggregateTo</b> value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
 				}
 
 				if ($aggregate_tag['AggregateTo'] == '#PARENT#') {
 					$aggregate_tag['AggregateTo'] = $config['ParentPrefix'];
 				}
 			}
 
 			$aggregate_tag['LocalPrefix'] = $config['Prefix'];
 			$this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag));
 		}
 	}
 
 	function ValidateConfig($prefix)
 	{
 		global $debugger;
 
 		$config =& $this->configData[$prefix];
 
 		$tablename = $config['TableName'];
 		$float_types = Array ('float', 'double', 'numeric');
 
 		$table_found = $this->Conn->Query('SHOW TABLES LIKE "'.$tablename.'"');
 		if (!$table_found) {
 			// config present, but table missing, strange
 			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 			$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$tablename</strong> missing, but prefix <b>".$config['Prefix']."</b> requires it!");
 			$debugger->WarningCount++;
 
 			return ;
 		}
 
 		$res = $this->Conn->Query('DESCRIBE '.$tablename);
 		$config_link = $debugger->getFileLink(FULL_PATH.$this->prefixFiles[$config['Prefix']], 1, $config['Prefix']);
 
 		$error_messages = Array (
 			'field_not_found' => 'Field <strong>%s</strong> exists in the database, but <strong>is not defined</strong> in config',
 			'default_missing' => 'Default value for field <strong>%s</strong> not set in config',
 			'not_null_error1' => 'Field <strong>%s</strong> is NOT NULL in the database, but is not configured as not_null', // or required',
 			'not_null_error2' => 'Field <strong>%s</strong> is described as NOT NULL in config, but <strong>does not have DEFAULT value</strong>',
 			'not_null_error3' => 'Field <strong>%s</strong> is described as <strong>NOT NULL in config</strong>, but is <strong>NULL in db</strong>',
 			'invalid_default' => '<strong>Default value</strong> for field %s<strong>%s</strong> not sync. to db (in config = %s, in db = %s)',
 			'date_column_not_null_error' => 'Field <strong>%s</strong> must be NULL in config and database, since it contains date',
 			'user_column_default_error' => 'Field <strong>%s</strong> must be have NULL as default value, since it holds user id',
 			'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
 			'virtual_type_missing' => '<strong>Type definition</strong> for virtual field <strong>%s</strong> missing in config',
 			'virtual_default_missing' => 'Default value for virtual field <strong>%s</strong> not set in config',
 			'virtual_not_null_error' => 'Virtual field <strong>%s</strong> cannot be not null, since it doesn\'t exist in database',
 			'invalid_calculated_field' => 'Calculated field <strong>%s</strong> is missing corresponding virtual field',
 		);
 
 		$config_errors = Array ();
 		$tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix
 
 		// validate unit config field declaration in relation to database table structure
 		foreach ($res as $field) {
 			$f_name = $field['Field'];
 
 			if (getArrayValue($config, 'Fields')) {
 				if (preg_match('/l[\d]+_[\w]/', $f_name)) {
 					// skip multilingual fields
 					continue;
 				}
 
 				if (!array_key_exists ($f_name, $config['Fields'])) {
 					$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
 				}
 				else {
 					$db_default = $field['Default'];
 
 					if (is_numeric($db_default)) {
 						$db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default;
 					}
 
 					$default_missing = false;
 					$options = $config['Fields'][$f_name];
 					$not_null = isset($options['not_null']) && $options['not_null'];
 					$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
 
 					if (!array_key_exists('default', $options)) {
 						$config_errors[] = sprintf($error_messages['default_missing'], $f_name);
 						$default_missing = true;
 					}
 
 					if ($field['Null'] != 'YES') {
 						// field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "")
 						if ( $f_name != $config['IDField'] && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
 							$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
 						}
 						if ($not_null && !isset($options['default']) ) {
 							$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
 						}
 					}
 					elseif ($not_null) {
 						$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
 					}
 
 					if (($formatter == 'kDateFormatter') && $not_null) {
 						$config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name);
 					}
 
 					// columns, holding userid should have NULL as default value
 					if (array_key_exists('type', $options) && !$default_missing) {
 						// both type and default value set
 
 						if (preg_match('/ById$/', $f_name) && $options['default'] !== null) {
 							$config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name);
 						}
 					}
 
 					if (!array_key_exists('type', $options)) {
 						$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
 					}
 
 					if (!$default_missing && ($field['Type'] != 'text')) {
 						if ( is_null($db_default) && $not_null ) {
 							$db_default = $options['type'] == 'string' ? '' : 0;
 						}
 
 						if ($f_name == $config['IDField'] && $options['type'] != 'string' && $options['default'] !== 0) {
 							$config_errors[] = sprintf($error_messages['invalid_default'], '<span class="debug_error">IDField</span> ', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
 						}
 						else if (((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types)) {
 							$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($db_default));
 						}
 					}
 				}
 			}
 		}
 
 		// validate virtual fields
 		if ( array_key_exists('VirtualFields', $config) ) {
 			foreach ($config['VirtualFields'] as $f_name => $options) {
 				if (!array_key_exists('type', $options)) {
 					$config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name);
 				}
 
 				if (array_key_exists('not_null', $options)) {
 					$config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name);
 				}
 
 				if (!array_key_exists('default', $options)) {
 					$config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name);
 				}
 			}
 		}
 
 		// validate calculated fields
 		if ( array_key_exists('CalculatedFields', $config) ) {
 			foreach ($config['CalculatedFields'] as $special => $calculated_fields) {
 				foreach ($calculated_fields as $calculated_field => $calculated_field_expr) {
 					if ( !isset($config['VirtualFields'][$calculated_field]) ) {
 						$config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field);
 					}
 				}
 			}
 
 			$config_errors = array_unique($config_errors);
 		}
 
 		if ($config_errors) {
 			$error_prefix = '<strong class="debug_error">Config Error'.(count($config_errors) > 1 ? 's' : '').': </strong> for prefix <strong>'.$config_link.'</strong> ('.$tablename.') in unit config:<br />';
 			$config_errors = $error_prefix.'&nbsp;&nbsp;&nbsp;'.implode('<br />&nbsp;&nbsp;&nbsp;', $config_errors);
 
 			kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 			$debugger->appendHTML($config_errors);
 			$debugger->WarningCount++;
 		}
 	}
 
 	function varDump($value)
 	{
 		return '<strong>'.var_export($value, true).'</strong> of '.gettype($value);
 	}
 
 	protected function ProcessDependencies($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$dependencies = getArrayValue($config, '_Dependencies');
 		/* @var $dependencies Array */
 
 		if ( !$dependencies ) {
 			return ;
 		}
 
 		foreach ($dependencies as $real_class => $requires) {
 			foreach ($requires as $class) {
 				$this->Application->registerDependency($real_class, $class);
 			}
 		}
 
 		unset($config['_Dependencies']);
 	}
 
 	function postProcessConfig($prefix, $config_key, $dst_prefix_var)
 	{
 		$main_config =& $this->configData[$prefix];
 		$sub_configs = isset($main_config[$config_key]) && $main_config[$config_key] ? $main_config[$config_key] : Array ();
 		if ( !$sub_configs ) {
 			return Array ();
 		}
 		unset($main_config[$config_key]);
 
 		$processed = array();
 		foreach ($sub_configs as $sub_prefix => $sub_config) {
 			if ($config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix])) {
 				$this->loadConfig($sub_prefix);
 			}
 			$sub_config['Prefix'] = $sub_prefix;
 			$this->configData[$sub_prefix] = kUtil::array_merge_recursive($this->configData[$$dst_prefix_var], $sub_config);
 
 			// when merging empty array to non-empty results non-empty array, but empty is required
 			foreach ($sub_config as $sub_key => $sub_value) {
 				if (!$sub_value) {
 					unset($this->configData[$sub_prefix][$sub_key]);
 				}
 			}
 			if ($config_key == 'Clones') {
 				$this->prefixFiles[$sub_prefix] = $this->prefixFiles[$prefix];
 			}
 
 			$this->postProcessConfig($sub_prefix, $config_key, $dst_prefix_var);
 			if ($config_key == 'AggregateConfigs') {
 				$processed = array_merge($this->postProcessConfig($sub_prefix, 'Clones', 'prefix'), $processed);
 			}
 			elseif ($this->ProcessAllConfigs) {
 				$this->parseConfig($sub_prefix);
 			}
 			array_push($processed, $sub_prefix);
 		}
 
 		if (!$prefix) {
 			// configs, that used only for cloning & not used ifself
 			unset($this->configData[$prefix]);
 		}
 		return array_unique($processed);
 	}
 
 	function PreloadConfigFile($filename)
 	{
 		$config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename);
 
 		if (defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES) {
 			if ( in_array($filename, get_included_files()) ) {
 				return '';
 			}
 
 			global $debugger;
 
 			if ($config_found) {
 				$file = FULL_PATH . $filename;
 				$file_crc = crc32($file);
 
 				$debugger->ProfileStart('inc_' . $file_crc, $file);
 				include_once($file);
 				$debugger->ProfileFinish('inc_' . $file_crc);
 				$debugger->profilerAddTotal('includes', 'inc_' . $file_crc);
 			}
 		}
 		elseif ($config_found) {
 			include_once(FULL_PATH . $filename);
 		}
 
 		if ($config_found) {
 			if (isset($config) && $config) {
 				// config file is included for 1st time -> save it's content for future processing
 				$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
 
 				preg_match($this->_moduleFolderRegExp, $filename, $rets);
 				$config['ModuleFolder'] = str_replace(DIRECTORY_SEPARATOR, '/', $rets[1]);
 				$config['BasePath'] = dirname(FULL_PATH . $filename);
 
 				if (array_key_exists('AdminTemplatePath', $config)) {
 					// append template base folder for admin templates path of this prefix
 					$module_templates = $rets[1] == 'core' ? '' : substr($rets[1], 8) . '/';
 					$config['AdminTemplatePath'] = $module_templates . $config['AdminTemplatePath'];
 				}
 
 				if (array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename)) {
 					trigger_error(
 						'Single unit config prefix "<strong>' . $prefix . '</strong>" ' .
 						'is used in multiple unit config files: ' .
 						'"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"',
 						E_USER_WARNING
 					);
 				}
 
 				$this->configData[$prefix] = $config;
 				$this->prefixFiles[$prefix] = $filename;
 
 				return $prefix;
 			}
 			else {
 				$prefix = array_search($filename, $this->prefixFiles);
 
 				if ( $prefix ) {
 					// attempt is made to include config file twice or more, but include_once prevents that,
 					// but file exists on hdd, then it is already saved to all required arrays, just return it's prefix
 					return $prefix;
 				}
 			}
 		}
 
 		return 'dummy';
 	}
 
 	function loadConfig($prefix)
 	{
 		if ( !isset($this->prefixFiles[$prefix]) ) {
 			throw new Exception('Configuration file for prefix <strong>' . $prefix . '</strong> is unknown');
 
 			return ;
 		}
 
 		$file = $this->prefixFiles[$prefix];
 		$prefix = $this->PreloadConfigFile($file);
 
 		if ($this->FinalStage) {
 			// run prefix OnAfterConfigRead so all
 			// hooks to it can define their clonses
 			$this->runAfterConfigRead($prefix);
 		}
 
 		$clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 		$clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones);
 
 		if ($this->FinalStage) {
 			$clones = array_unique($clones);
 
 			foreach ($clones as $a_prefix) {
 				$this->runAfterConfigRead($a_prefix);
 			}
 		}
 	}
 
 	function runAfterConfigRead($prefix)
 	{
 		if (in_array($prefix, $this->AfterConfigProcessed)) {
 			return ;
 		}
 
 		$this->Application->HandleEvent( new kEvent($prefix . ':OnAfterConfigRead') );
 
 		if (!(defined('IS_INSTALL') && IS_INSTALL)) {
 			// allow to call OnAfterConfigRead multiple times during install
 			array_push($this->AfterConfigProcessed, $prefix);
 		}
 	}
 
 	/**
 	 * Reads unit (specified by $prefix)
 	 * option specified by $option
 	 *
 	 * @param string $prefix
 	 * @param string $name
 	 * @param mixed $default
 	 * @return string
 	 * @access public
 	 */
 	function getUnitOption($prefix, $name, $default = false)
 	{
 		if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) {
 			if (!isset($this->configData[$rets[1]])) {
 				$this->loadConfig($rets[1]);
 			}
 			$ret = isset($this->configData[$rets[1]][$name][$rets[2]]) ? $this->configData[$rets[1]][$name][$rets[2]] : false;
 //			$ret = getArrayValue($this->configData, $rets[1], $name, $rets[2]);
 		}
 		else {
 			if (!isset($this->configData[$prefix])) {
 				$this->loadConfig($prefix);
 			}
 			$ret = isset($this->configData[$prefix][$name]) ? $this->configData[$prefix][$name] : false;
 //			$ret = getArrayValue($this->configData, $prefix, $name);
 		}
 		return $ret === false ? $default : $ret;
 	}
 
 	/**
 	 * Read all unit with $prefix options
 	 *
 	 * @param string $prefix
 	 * @return Array
 	 * @access public
 	 */
 	function getUnitOptions($prefix)
 	{
 		if (!isset($this->configData[$prefix])) {
 			$this->loadConfig($prefix);
 		}
 
 		return $this->configData[$prefix];
 	}
 
 	/**
 	 * Set's new unit option value
 	 *
 	 * @param string $prefix
 	 * @param string $name
 	 * @param string $value
 	 * @access public
 	 */
 	function setUnitOption($prefix, $name, $value)
 	{
 		if ( preg_match('/(.*)\.(.*)/', $prefix, $rets) ) {
 			if ( !isset($this->configData[$rets[1]]) ) {
 				$this->loadConfig($rets[1]);
 			}
 
 			$this->configData[$rets[1]][$name][$rets[2]] = $value;
 		}
 		else {
 			if ( !isset($this->configData[$prefix]) ) {
 				$this->loadConfig($prefix);
 			}
 
 			$this->configData[$prefix][$name] = $value;
 		}
 	}
 
 	protected function getClasses($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass');
 		$register_classes = isset($config['RegisterClasses']) ? $config['RegisterClasses'] : Array ();
 
 		foreach ($class_params as $param_name) {
 			if ( !isset($config[$param_name]) ) {
 				continue;
 			}
 
 			$config[$param_name]['pseudo'] = $this->getPseudoByOptionName($param_name, $prefix);
 			$register_classes[] = $config[$param_name];
 		}
 
 		return $register_classes;
 	}
 
 	protected function getPseudoByOptionName($option_name, $prefix)
 	{
 		$pseudo_class_map = Array (
 			'ItemClass' => '%s',
 			'ListClass' => '%s_List',
 			'EventHandlerClass' => '%s_EventHandler',
 			'TagProcessorClass' => '%s_TagProcessor'
 		);
 
 		return sprintf($pseudo_class_map[$option_name], $prefix);
 	}
 
 	/**
 	 * Get's config file name based
 	 * on folder name supplied
 	 *
 	 * @param string $folderPath
 	 * @return string
 	 * @access private
 	 */
 	function getConfigName($folderPath)
 	{
 		return $folderPath . DIRECTORY_SEPARATOR . basename($folderPath) . '_config.php';
 	}
 
 	/**
 	 * Checks if config file is allowed for includion (if module of config is installed)
 	 *
 	 * @param string $config_path relative path from in-portal directory
 	 */
 	function configAllowed($config_path)
 	{
 		static $module_paths = null;
 
 		if (defined('IS_INSTALL') && IS_INSTALL) {
 			// at installation start no modules in db and kernel configs could not be read
 			return true;
 		}
 
 		if (preg_match('#^' . $this->_directorySeparator . 'core#', $config_path)) {
 			// always allow to include configs from "core" module's folder
 			return true;
 		}
 
 		if (!$this->Application->ModuleInfo) {
 			return false;
 		}
 
 		if (!isset($module_paths)) {
 			$module_paths = Array ();
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				$module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/'));
 			}
 
 			$module_paths = array_unique($module_paths);
 		}
 
 		preg_match($this->_moduleFolderRegExp, $config_path, $rets);
 
 		// config file path starts with module folder path
 		return in_array($rets[1], $module_paths);
 	}
 
 	/**
 	 * Returns true if config exists and is allowed for reading
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
 	function prefixRegistred($prefix)
 	{
 		return isset($this->prefixFiles[$prefix]) ? true : false;
 	}
 
 	/**
 	 * Returns config file for given prefix
 	 *
 	 * @param string $prefix
 	 * @return string
 	 */
 	function getPrefixFile($prefix)
 	{
 		return array_key_exists($prefix, $this->prefixFiles) ? $this->prefixFiles[$prefix] : false;
 	}
 
 	function iterateConfigs($callback_function, $params)
 	{
 		$this->includeConfigFiles(MODULES_PATH); //make sure to re-read all configs
 		$this->AfterConfigRead();
 
 		foreach ($this->configData as $prefix => $config_data) {
 			$callback_function[0]->$callback_function[1]($prefix, $config_data, $params);
 		}
 	}
 
 }
\ No newline at end of file
Index: branches/5.2.x/core/units/configuration/configuration_event_handler.php
===================================================================
--- branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 14786)
+++ branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 14787)
@@ -1,299 +1,292 @@
 <?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 ConfigurationEventHandler extends kDBEventHandler  {
 
 		/**
 		 * Changes permission section to one from REQUEST, not from config
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(&$event)
 		{
 			$event->setEventParam('PermSection', $this->Application->GetVar('section'));
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			$module = $this->Application->GetVar('module');
 			$section = $this->Application->GetVar('section');
 
 			$object->addFilter('module_filter', '%1$s.ModuleOwner = '.$this->Conn->qstr($module));
 			$object->addFilter('section_filter', '%1$s.Section = '.$this->Conn->qstr($section));
 
 			if (!$this->Application->ConfigValue('AllowAdminConsoleInterfaceChange')) {
 				$object->addFilter('interface_change_filter', '%1$s.VariableName <> "AdminConsoleInterface"');
 			}
 
 			if (defined('IS_INSTALL') && IS_INSTALL) {
 				$object->addFilter('install_filter', '%1$s.Install = 1');
 			}
 
 			$object->addFilter('visible_filter', '%1$s.Heading <> ""');
 		}
 
 		/**
 		 * Performs validation of configuration variable value
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(&$event)
 		{
 			static $default_field_options = null;
 
 			parent::OnBeforeItemUpdate($event);
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			// ability to validate each configuration variable separately
 			if ( !isset($default_field_options) ) {
 				$default_field_options = $object->GetFieldOptions('VariableValue');
 			}
 
 			$new_field_options = $default_field_options;
 			$validation = $object->GetDBField('Validation');
 
 			if ( $validation ) {
 				$new_field_options = array_merge($new_field_options, unserialize($validation));
 			}
 
 			$object->SetFieldOptions('VariableValue', $new_field_options);
 
 			// if password field is empty, then don't update
 			if ( $object->GetDBField('ElementType') == 'password' ) {
 				if ( trim($object->GetDBField('VariableValue')) == '' ) {
 					$field_options = $object->GetFieldOptions('VariableValue');
 					$field_options['skip_empty'] = 1;
 					$object->SetFieldOptions('VariableValue', $field_options);
 				}
 				else {
 					$password_formatter =& $this->Application->recallObject('kPasswordFormatter');
 					/* @var $password_formatter kPasswordFormatter */
 
 					$object->SetDBField('VariableValue', $password_formatter->EncryptPassword($object->GetDBField('VariableValue'), 'b38'));
 				}
 			}
 
 			$field_name = $object->GetDBField('VariableName');
 			$field_values = $this->Application->GetVar($event->getPrefixSpecial(true));
 			$state_country_hash = Array ('Comm_State' => 'Comm_Country', 'Comm_Shipping_State' => 'Comm_Shipping_Country');
 
 			if ( array_key_exists($field_name, $state_country_hash) ) {
 				// if this is state field
 				$sql = 'SELECT VariableId
 						FROM ' . $this->Application->getUnitOption('conf', 'TableName') . '
 						WHERE VariableName = "' . $state_country_hash[$field_name] . '"';
 				$country_variable_id = $this->Conn->GetOne($sql);
 
 				$check_state = $object->GetDBField('VariableValue');
 				$check_country = $field_values[$country_variable_id]['VariableValue'];
 
 				if ( !$check_country || !$check_state ) {
 					return;
 				}
 
 				$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
 				/* @var $cs_helper kCountryStatesHelper */
 
 				$state_iso = $cs_helper->getStateIso($check_state, $check_country);
 
 				if ( $state_iso !== false ) {
 					$object->SetDBField('VariableValue', $state_iso);
 				}
 				else {
 					// selected state doesn't belong to selected country
 					$object->SetError('VariableValue', 'invalid_state', 'la_InvalidState');
 				}
 			}
 
 			if ( $object->GetDBField('VariableName') == 'AdminConsoleInterface' ) {
 				$can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange');
 
 				if ( ($object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue')) && !$can_change ) {
 					$object->SetError('VariableValue', 'not_allowed', 'la_error_OperationNotAllowed');
 				}
 			}
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
+			static $skin_deleted = false;
+
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
-			if ($object->GetDBField('ElementType') == 'password') {
-				if (trim($object->GetDBField('VariableValue')) == '') {
+			if ( $object->GetDBField('ElementType') == 'password' ) {
+				if ( trim($object->GetDBField('VariableValue')) == '' ) {
 					$field_options = $object->GetFieldOptions('VariableValue');
 					unset($field_options['skip_empty']);
 					$object->SetFieldOptions('VariableValue', $field_options);
 				}
 			}
 
 			// allows to check if variable's value was changed now
 			$variable_name = $object->GetDBField('VariableName');
-			$variable_value = $object->GetDBField('VariableValue');
-			$watch_variables = Array (
-				'Require_AdminSSL', 'AdminSSL_URL', 'AdvancedUserManagement',
-				'Site_Name', 'AdminConsoleInterface', 'UsePopups'
-			);
+			$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 
-			if (in_array($variable_name, $watch_variables)) {
-				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
+			if ( $object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue') ) {
+				$changed[] = $variable_name;
+				$this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed);
+			}
+
+			if ( $variable_name == 'Require_AdminSSL' || $variable_name == 'AdminSSL_URL' ) {
+				// when administrative console is moved to SSL mode, then delete skin
+				if ( in_array($variable_name, $changed) && !$skin_deleted ) {
+					$skin_helper =& $this->Application->recallObject('SkinHelper');
+					/* @var $skin_helper SkinHelper */
+
+					$skin_file = $skin_helper->getSkinPath();
+					if ( file_exists($skin_file) ) {
+						unlink($skin_file);
+					}
 
-				if ($variable_value != $object->GetOriginalField('VariableValue')) {
-					$changed[] = $variable_name;
-					$this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed);
-				}
-
-				switch ($variable_name) {
-					case 'Require_AdminSSL':
-					case 'AdminSSL_URL':
-						static $skin_deleted = false;
-
-						if (in_array($variable_name, $changed) && !$skin_deleted) {
-							// when administrative console is moved to SSL mode, then delete skin
-							$skin_helper =& $this->Application->recallObject('SkinHelper');
-							/* @var $skin_helper SkinHelper */
-
-							$skin_file = $skin_helper->getSkinPath();
-							if (file_exists($skin_file)) {
-								unlink($skin_file);
-							}
-
-							$skin_deleted = true;
-						}
-						break;
+					$skin_deleted = true;
 				}
 			}
 
 			$this->Application->StoreVar('config_was_updated', 1);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdate(&$event)
 		{
-			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
+			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
-				return ;
+				return;
 			}
 
-			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			// 1. save user selected module root category
 			$new_category_id = getArrayValue($items_info, 'ModuleRootCategory', 'VariableValue');
-			if ($new_category_id !== false) {
+			if ( $new_category_id !== false ) {
 				unset($items_info['ModuleRootCategory']);
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
-			if ($items_info) {
+			if ( $items_info ) {
 				$has_error = false;
+
 				foreach ($items_info as $id => $field_values) {
 					$object->Clear(); // clear validation errors from previous variable
 					$object->Load($id);
-	 				$object->SetFieldsFromHash($field_values);
+					$object->SetFieldsFromHash($field_values);
 
-					if (!$object->Update($id)) {
+					if ( !$object->Update($id) ) {
 						// don't stop when error found !
 						$has_error = true;
 					}
 				}
 
 				$event->status = $has_error ? kEvent::erFAIL : kEvent::erSUCCESS;
 			}
 
-			if ($event->status == kEvent::erSUCCESS) {
-				if ($new_category_id !== false) {
+			if ( $event->status == kEvent::erSUCCESS ) {
+				if ( $new_category_id !== false ) {
 					// root category was submitted
 					$module = $this->Application->GetVar('module');
 					$root_category_id = $this->Application->findModule('Name', $module, 'RootCat');
 
-					if ($root_category_id != $new_category_id) {
+					if ( $root_category_id != $new_category_id ) {
 						// root category differs from one in db
-						$fields_hash = Array('RootCat' => $new_category_id);
-						$this->Conn->doUpdate($fields_hash, TABLE_PREFIX.'Modules', 'Name = '.$this->Conn->qstr($module));
+						$fields_hash = Array ('RootCat' => $new_category_id);
+						$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', 'Name = ' . $this->Conn->qstr($module));
 					}
 				}
 
 				// reset cache
 				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
-				$require_refresh = Array (
-					'AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups'
-				);
+				$require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups');
 
 				$refresh_sections = array_intersect($require_refresh, $changed);
 				$require_full_refresh = Array ('Site_Name', 'AdminConsoleInterface');
 
-				if (array_intersect($require_full_refresh, $changed)) {
+				if ( array_intersect($require_full_refresh, $changed) ) {
 					$event->SetRedirectParam('refresh_all', 1);
-				} elseif ($refresh_sections) {
-					// reset sections too, because of AdvancedUserManagement
+				}
+				elseif ( $refresh_sections ) {
 					$event->SetRedirectParam('refresh_tree', 1);
 				}
 
-				$this->Application->DeleteUnitCache($refresh_sections ? true : false);
+				if ( $refresh_sections ) {
+					// reset sections too, because of AdvancedUserManagement
+					$this->Application->DeleteSectionCache();
+				}
+
+				$this->Application->DeleteUnitCache($changed);
 			}
-			elseif ($this->Application->GetVar('errors_' . $event->getPrefixSpecial())) {
+			elseif ( $this->Application->GetVar('errors_' . $event->getPrefixSpecial()) ) {
 				// because we have list out there, and this is item
-				$this->Application->removeObject( $event->getPrefixSpecial() );
+				$this->Application->removeObject($event->getPrefixSpecial());
 			}
 
 			// keeps module and section in REQUEST to ensure, that last admin template will work
 			$event->SetRedirectParam('module', $this->Application->GetVar('module'));
 			$event->SetRedirectParam('section', $this->Application->GetVar('section'));
 		}
 
 		/**
 		 * Process items from selector (selected_ids var, key - prefix, value - comma separated ids)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnProcessSelected(&$event)
 		{
 			$selected_ids = $this->Application->GetVar('selected_ids');
 			$this->Application->StoreVar('ModuleRootCategory', $selected_ids['c']);
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 	}
\ No newline at end of file
Index: branches/5.2.x/core/units/modules/modules_event_handler.php
===================================================================
--- branches/5.2.x/core/units/modules/modules_event_handler.php	(revision 14786)
+++ branches/5.2.x/core/units/modules/modules_event_handler.php	(revision 14787)
@@ -1,161 +1,163 @@
 <?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 ModulesEventHandler extends kDBEventHandler {
 
 		/**
 		 * Builds item
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnItemBuild(&$event)
 		{
 			$this->Application->SetVar($event->getPrefixSpecial(true).'_id', $event->Special);
 			parent::OnItemBuild($event);
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			if ($event->Special) {
 				$object->addFilter('current_module', '%1$s.Name = '.$event->Special);
 			}
 
 			$object->addFilter('not_core', '%1$s.Name <> "Core"');
 		}
 
 		function mapEvents()
 		{
 			parent::mapEvents();
 			$this->eventMethods['OnMassApprove'] = 'moduleAction';
 			$this->eventMethods['OnMassDecline'] = 'moduleAction';
 		}
 
 		/**
 		 * Disabled modules, but not In-Portal
 		 *
 		 * @param kEvent $event
 		 */
 		function moduleAction(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return ;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			if (!$ids) {
 				return ;
 			}
 
 			$updated = 0;
 			$status_field = $this->Application->getUnitOption($event->Prefix, 'StatusField');
 			$status_field = array_shift($status_field);
 
 			foreach ($ids as $id) {
 				$object->Load($id);
 
 				if (in_array($id, Array ('In-Portal', 'Core')) || !$object->isLoaded()) {
 					// don't allow any kind of manupulations with kernel
 					// approve/decline on not installed module
 					continue;
 				}
 
 				$enabled = $event->Name == 'OnMassApprove' ? 1 : 0;
 				$object->SetDBField($status_field, $enabled);
 
 				if (!$object->GetChangedFields()) {
 					// no changes -> skip
 					continue;
 				}
 
 				if ($object->Update()) {
 					$updated++;
 
 					$sql = 'UPDATE ' . TABLE_PREFIX . 'ImportScripts
 							SET Status = ' . $enabled . '
  							WHERE Module = "' . $object->GetDBField('Name') . '"';
  					$this->Conn->Query($sql);
 				}
 				else {
 					$event->status = kEvent::erFAIL;
 					$event->redirect = false;
 					break;
 				}
 			}
 
-			if ($updated) {
+			if ( $updated ) {
 				$event->status = kEvent::erSUCCESS;
 				$event->setRedirectParams(Array ('opener' => 's'), true);
 
-				$this->Application->DeleteUnitCache(true); //true to reset sections cache also
+				$this->Application->DeleteUnitCache();
+				$this->Application->DeleteSectionCache();
+
 				$event->SetRedirectParam('RefreshTree', 1);
 			}
 		}
 
 		/**
-		 * Occures after list is queried
+		 * Occurs after list is queried
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterListQuery(&$event)
 		{
 			parent::OnAfterListQuery($event);
 
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			$new_modules = $modules_helper->getModules(kModulesHelper::NOT_INSTALLED);
 
 			if (!$new_modules || $this->Application->RecallVar('user_id') != USER_ROOT) {
 				return ;
 			}
 
 			require_once FULL_PATH . '/core/install/install_toolkit.php';
 
 			$toolkit = new kInstallToolkit();
 
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			foreach ($new_modules as $module) {
 				$module_record = Array (
 					'Name' => $toolkit->getModuleName($module),
 					'Path' => 'modules/' . $module . '/',
 					'Version' => $toolkit->GetMaxModuleVersion('modules/' . $module . '/'),
 					'Loaded' => 0,
 					'BuildDate' => null,
 				);
 
 				$object->addRecord($module_record);
 			}
 		}
 	}
\ No newline at end of file