Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php	(revision 14183)
+++ branches/5.2.x/core/kernel/application.php	(revision 14184)
@@ -1,3332 +1,2523 @@
 <?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 guranteed by NOT calling the class constuctor directly, but rather calling kApplication::Instance() method,
 * which returns an instance of the application. The method gurantees that it will return exactly the same instance for any call.<br>
 * See singleton pattern by GOF.
 */
 class kApplication implements kiCacheable {
 
 	/**
 	 * 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;
 
 	/**
 	 * Prevents request from beeing proceeded twice in case if application init is called mere then one time
 	 *
 	 * @var bool
 	 * @todo This is not good anyway (by Alex)
 	 */
 	var $RequestProcessed = false;
 
 	/**
 	* The main Factory used to create
 	* almost any class of kernel and
 	* modules
 	*
 	* @access private
 	* @var kFactory
 	*/
 	var $Factory;
 
 	/**
-	 * All ConfigurationValues table content (hash) here
-	 *
-	 * @var Array
-	 * @access private
-	 */
-	var $ConfigHash = Array();
-
-	/**
-	 * Ids of config variables used in current run (for caching)
-	 *
-	 * @var Array
-	 * @access private
-	 */
-	var $ConfigCacheIds = array();
-
-	/**
 	 * 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 refererence 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;
 
 	/**
-	 * Ref for TemplatesChache
+	 * Holds a ref to kUrlManager
 	 *
-	 * @var TemplatesCache
+	 * @var kUrlManager
+	 * @access protected
 	 */
-	var $TemplatesCache = null;
+	protected $UrlManager = null;
 
 	/**
-	 * Physical template name mapping to their template names based on structure
+	 * Ref for TemplatesChache
 	 *
-	 * @var Array
+	 * @var TemplatesCache
 	 */
-	var $structureTemplateMapping = Array ();
+	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 memory caching class
+	 * Object of unit caching class
 	 *
-	 * @var kCache
+	 * @var kCacheManager
 	 */
-	var $memoryCache = null;
+	var $cacheManager = null;
 
 	/**
 	 * Tells, that administrator has authentificated in administrative console
 	 * Should be used to manipulate data change OR data restrictioning!
 	 *
 	 * @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 guranteed 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 descendand 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.
  	* @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->Conn = new kDBConnection(SQL_TYPE, Array(&$this, 'handleSQLError') );
 		$this->Conn->debugMode = $this->isDebugMode();
 		$this->Conn->Connect(SQL_SERVER, SQL_USER, SQL_PASS, SQL_DB);
 
 		$this->Factory = new kFactory();
 		$this->registerDefaultClasses();
 		$this->Phrases = new PhrasesCache();
-		$this->memoryCache =& $this->Factory->makeClass('Cache');
-		$this->EventManager =& $this->Factory->makeClass('EventManager');
+
+		$this->cacheManager =& $this->makeClass('kCacheManager');
+		$this->cacheManager->InitCache();
+
+		$this->UrlManager =& $this->makeClass('kUrlManager');
+		$this->EventManager =& $this->makeClass('EventManager');
 		$this->Factory->Storage['EventManager'] =& $this->EventManager;
 		$this->RegisterDefaultBuildEvents();
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('Before UnitConfigReader');
 		}
 
 		$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
 		$this->UnitConfigReader->scanModules(MODULES_PATH);
 
 		$this->registerModuleConstants();
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('After UnitConfigReader');
 		}
 
 		define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
 
 		$this->HttpQuery =& $this->recallObject('HTTPQuery');
 
 		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');
 		}
 
 		if (!$this->RecallVar('UserGroups')) {
 			$user_groups = trim($this->Session->GetField('GroupList'), ',');
 			if (!$user_groups) {
 				$user_groups = $this->ConfigValue('User_GuestGroup');
 			}
 
 			$this->Session->SetField('GroupList', $user_groups);
 			$this->StoreVar('UserGroups', $user_groups, true); // true for optional
 		}
 
-		$this->LoadStructureTemplateMapping();
+		$this->UrlManager->LoadStructureTemplateMapping();
 		$this->HttpQuery->AfterInit();
 
 		$this->Session->ValidateExpired();
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
 		}
 
-		$this->LoadCache();
-		$this->InitConfig();
+		$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();
 
 		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) );
 		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;
 	}
 
 	/**
 	 * Returns module information. Searches module by requested field
 	 *
 	 * @param string $field
 	 * @param mixed $value
 	 * @param string field value to returns, if not specified, then return all fields
 	 * @param string field to return
 	 * @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;
 	}
 
 	function refreshModuleInfo()
 	{
 		if (defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules')) {
 			$this->registerModuleConstants();
 			return false;
 		}
 
 		$modules_helper =& $this->recallObject('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);
-
-		/*$theme_id = $this->GetVar('m_theme');
-		if (!$theme_id) {
-			$theme_id =  $this->GetDefaultThemeId();
-			if (!$theme_id) {
-				if (!defined('IS_INSTALL')) $this->ApplicationDie('No Primary Theme Selected');
-			}
-		}
-		$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');
-		if (!$theme->IsLoaded() || !$theme->GetDBField('Enabled')) {
-		if (!defined('IS_INSTALL')) $this->ApplicationDie('Unknown or disabled theme');
-		}
-
-		kUtil::safeDefine('THEMES_PATH', '/themes/'.$theme->GetDBField('Name'));*/
 	}
 
 	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');
 		if (!$theme->IsLoaded() || !$theme->GetDBField('Enabled')) {
 			return false;
 		}
 		$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('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php', 'kiCacheable');
 
 		$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
 
 		$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager', 'kiCacheable');
-		$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php', 'kHookManager', 'kiCacheable');
-		$this->registerClass('kAgentManager', KERNEL_PATH . '/managers/agent_manager.php', 'kAgentManager', 'kiCacheable');
-		$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php', 'kRequestManager');
+		$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('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php', null, 'kiCacheable');
 
 		$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
 
 		$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', 'Cache', 'Params');
+		$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
 		$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery', 'Params');
 
 		$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
 		$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
 
 		$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');
 
 		$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
 		$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php','m_TagProcessor', 'kTagProcessor');
 
 		$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
 		$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
 		$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
 		$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php', null, 'kTagProcessor');
 		$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
 		$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
 		$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
 		$this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php');
 
 		$this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php');
 		$this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php', null, Array ('kHelper', 'kDBTagProcessor'));
 
 		$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender', 'kHelper');
 		$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
 
-		if (file_exists(MODULES_PATH . '/in-commerce/units/currencies/currency_rates.php')) {
+		if ( file_exists(MODULES_PATH . '/in-commerce/units/currencies/currency_rates.php') ) {
 			$this->registerClass('kCurrencyRates', MODULES_PATH . '/in-commerce/units/currencies/currency_rates.php');
 		}
 
 		// do not move to config - this helper is used before configs are read
 		$this->registerClass('kModulesHelper', KERNEL_PATH . '/../units/helpers/modules_helper.php', '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
 	 */
-	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);
-	}
-
-	/**
-	 * Returns item's filename that corresponds id passed. If possible, then get it from cache
-	 *
-	 * @param string $prefix
-	 * @param int $id
-	 * @param int $category_id
-	 * @return string
-	 */
-	function getFilename($prefix, $id, $category_id = null)
+	public function getCategoryCache($category_id, $name)
 	{
-		if ($prefix == 'c') {
-			throw new Exception('Method "<strong>' . __FUNCTION__ . '</strong>" no longer work with "<strong>c</strong>" prefix. Please use "<strong>getCategoryCache</strong>" method instead');
-
-			return false;
-		}
-
-		$category_id = isset($category_id) ? $category_id : $this->GetVar('m_cat_id');
-
-		$cache_key = 'filenames[%' . $this->incrementCacheSerial($prefix, $id, false) . '%]:' . (int)$category_id;
-		$filename = $this->getCache($cache_key);
-
-		if ($filename === false) {
-			$this->Conn->nextQueryCachable = true;
-			$sql = 'SELECT ResourceId
-					FROM ' . $this->getUnitOption($prefix, 'TableName') . '
-					WHERE ' . $this->getUnitOption($prefix, 'IDField') . ' = ' . $this->Conn->qstr($id);
-			$resource_id = $this->Conn->GetOne($sql);
-
-			$this->Conn->nextQueryCachable = true;
-			$sql = 'SELECT Filename
-					FROM ' . TABLE_PREFIX . 'CategoryItems
-					WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')';
-			$filename = $this->Conn->GetOne($sql);
-
-			if ($filename !== false) {
-				$this->setCache($cache_key, $filename);
-			}
-		}
-
-		return $filename;
+		return $this->cacheManager->getCategoryCache($category_id, $name);
 	}
 
 	/**
 	 * Returns caching type (none, memory, temporary)
 	 *
 	 * @return int
+	 * @access public
 	 */
-	function isCachingType($caching_type)
+	public function isCachingType($caching_type)
 	{
-		return $this->memoryCache->getCachingType() == $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
 	 */
-	function incrementCacheSerial($prefix, $id = null, $increment = true)
+	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->isDebugMode()) {
-				$this->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 $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
+	}
 
-		return $serial_name;
+	/**
+	 * 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
+	 * @return mixed
+	 * @access public
+	 */
+	public function getCache($key, $store_locally = true)
+	{
+		return $this->cacheManager->getCache($key, $store_locally);
 	}
 
 	/**
 	 * 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
 	 */
-	function setCache($key, $value, $expiration = 0)
+	public function setCache($key, $value, $expiration = 0)
 	{
-		return $this->memoryCache->setCache($key, $value, $expiration);
+		return $this->cacheManager->setCache($key, $value, $expiration);
 	}
 
 	/**
-	 * Sets value to database cache
+	 * Deletes key from cache
 	 *
-	 * @param string $name
-	 * @param mixed $value
-	 * @param int $expiration
+	 * @param string $key
+	 * @access public
 	 */
-	function setDBCache($name, $value, $expiration = false)
+	public function deleteCache($key)
 	{
-		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');
+		$this->cacheManager->deleteCache($key);
 	}
 
 	/**
-	 * Returns cached $key value from cache named $cache_name
+	 * Reset's all memory cache at once
 	 *
-	 * @param int $key key name from cache
-	 * @param bool $store_locally store data locally after retrieved
-	 * @return mixed
+	 * @access public
 	 */
-	function getCache($key, $store_locally = true)
+	public function resetCache()
 	{
-		return $this->memoryCache->getCache($key, $store_locally);
+		$this->cacheManager->resetCache();
 	}
 
 	/**
 	 * Returns value from database cache
 	 *
 	 * @param string $name key name
 	 * @return mixed
+	 * @access public
 	 */
-	function getDBCache($name)
+	public 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;
+		return $this->cacheManager->getDBCache($name);
 	}
 
 	/**
-	 * Deletes key from cache
+	 * Sets value to database cache
 	 *
-	 * @param string $key
+	 * @param string $name
+	 * @param mixed $value
+	 * @param int $expiration
+	 * @access public
 	 */
-	function deleteCache($key)
+	public function setDBCache($name, $value, $expiration = false)
 	{
-		$this->memoryCache->delete($key);
+		$this->cacheManager->setDBCache($name, $value, $expiration);
 	}
 
 	/**
 	 * Deletes key from database cache
 	 *
 	 * @param string $name
+	 * @access public
 	 */
-	function deleteDBCache($name)
+	public function deleteDBCache($name)
 	{
-		$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Cache
-				WHERE VarName = ' . $this->Conn->qstr($name);
-		$this->Conn->Query($sql);
+		$this->cacheManager->deleteDBCache($name);
 	}
 
 	/**
 	 * Registers each module specific constants if any found
 	 *
 	 */
 	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_name => $module_info) {
 			$contants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
 
 			if (file_exists($contants_file)) {
 				kUtil::includeOnce($contants_file);
 			}
 		}
 
 		return true;
 	}
 
 	function ProcessRequest()
 	{
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_SHOW_HTTPQUERY') ) {
 			$this->Debugger->appendHTML('HTTPQuery:');
 			$this->Debugger->dumpVars($this->HttpQuery->_Params);
 		}
 
 		$this->EventManager->ProcessRequest();
 		$this->EventManager->runAgents(reBEFORE);
 		$this->RequestProcessed = 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
 				define('DBG_SKIP_REPORTING', 1);
 			}
 		}
 		elseif ($this->GetVar('admin')) {
 			// viewing front-end through admin's frame
 			$admin_session =& $this->recallObject('Session.admin');
 			$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();
 
 		if (!$this->RequestProcessed) $this->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->memoryCache->printStatistics();
+				$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 < 0 || $compression_level > 9) {
 				$compression_level = 7;
 			}
 
 			header('Content-Encoding: gzip');
 			echo gzencode($this->HTML, $compression_level);
 		}
 		else {
 			echo $this->HTML;
 		}
 
-		$this->UpdateCache();
+		$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 longint
 	*/
 	function GetSID()
 	{
 		$session =& $this->recallObject('Session');
 		return $session->GetID();
 	}
 
 	function DestroySession()
 	{
 		$session =& $this->recallObject('Session');
 		$session->Destroy();
 	}
 
 	/**
 	* Returns variable passed to the script as GET/POST/COOKIE
 	*
 	* @access public
 	* @param string $name Name of variable to retrieve
 	* @param int $default default value returned in case if varible not present
 	* @return mixed
 	*/
 	function GetVar($name, $default = false)
 	{
 		return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$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
 	*/
 	function SetVar($var,$val)
 	{
 		return $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.
 	* @see RecallVar
 	* @access public
 	* @param string $var Variable name
 	* @param mixed $val Variable value
 	*/
 	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
 	*/
 	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 AddBlock($name, $tpl)
 	{
 		$this->cache[$name] = $tpl;
 	}
 
 	function ProcessParsedTag($prefix, $tag, $params)
 	{
 		$processor = $this->Parser->GetProcessor($prefix);
 
 		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 Array $pass_params Forces to pass current parser params to this block/template. Use with cauntion, because you can accidently pass "block_no_data" parameter.
 	 * @param bool $as_template
 	 * @return string
 	 */
 	function ParseBlock($params, $pass_params = 0, $as_template = false)
 	{
 		if (substr($params['name'], 0, 5) == 'html:') {
 			return substr($params['name'], 6);
 		}
 
 		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);
 	}
 
 	/**
-	 * Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all
-	 *
-	 * @param string $prefix
-	 * @param string $index_file
-	 * @param Array $params
-	 * @return string
-	 */
-	function getIndexFile($prefix, $index_file, &$params)
-	{
-		if (isset($params['index_file'])) {
-			$index_file = $params['index_file'];
-			unset($params['index_file']);
-			return $index_file;
-		}
-
-		if (isset($index_file)) {
-			return $index_file;
-		}
-
-		if (defined('INDEX_FILE')) {
-			return INDEX_FILE;
-		}
-
-		$cut_prefix = trim(BASE_PATH, '/').'/'.trim($prefix, '/');
-		return trim(preg_replace('/'.preg_quote($cut_prefix, '/').'(.*)/', '\\1', $_SERVER['PHP_SELF']), '/');
-	}
-
-	/**
 	* Return href for template
 	*
 	* @access public
 	* @param string $t Template path
 	* @var string $prefix index.php prefix - could be blank, 'admin'
 	*/
-	function HREF($t, $prefix = '', $params = null, $index_file = null)
+	public function HREF($t, $prefix = '', $params = null, $index_file = null)
 	{
-		static $theme_id = null;
-
-		if (!isset($theme_id)) {
-			$theme_id = $this->GetVar('m_theme');
-		}
-
-		if (!$t) {
-			// when template not specified, use current
-			$t = $this->GetVar('t');
-		}
-
-		$t = preg_replace('/^Content\//i', '', $t);
-
-		if (substr($t, -4) == '.tpl') {
-			// cut template extension (deprecated link format)
-			$t = substr($t, 0, strlen($t) - 4);
-		}
-
-		if (substr($t, 0, 3) == 'id:') {
-			// link to structure page using it's id
-			$params['m_cat_id'] = substr($t, 3);
-			$t = $this->structureTemplateMapping[$t];
-		}
-
-		if (array_key_exists('use_section', $params)) {
-			$use_section = $params['use_section'];
-			unset($params['use_section']);
-		}
-
-		if (isset($use_section) && $use_section && array_key_exists($t . ':' . $theme_id, $this->structureTemplateMapping)) {
-			// structure template corresponding to given physical template
-			$t = $this->structureTemplateMapping[$t . ':' . $theme_id];
-			unset($params['use_section']);
-		}
-
-		if (preg_match('/external:(.*)/', $t, $rets)) {
-			// external url
-			return $rets[1];
-		}
-
-		if ($this->isAdmin && $prefix == '') $prefix = ADMIN_DIRECTORY;
-		if ($this->isAdmin && $prefix == '_FRONT_END_') $prefix = '';
-
-		$index_file = $this->getIndexFile($prefix, $index_file, $params);
-
-		if (isset($params['_auto_prefix_'])) {
-			unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here
-		}
-
-		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
-		if ($ssl !== null) {
-			$session =& $this->recallObject('Session');
-			/* @var $session Session */
-
-			$target_url = rtrim($this->BaseURL('', $ssl, false), '/');
-			$cookie_url = trim($session->CookieDomain . $session->CookiePath, '/.');
-
-			// set session to GET_ONLY, to pass sid only if sid is REAL AND session is set
-			if (!preg_match('#' . preg_quote($cookie_url) . '#', $target_url) && $session->SessionSet) {
-				// when SSL<->NON-SSL redirect to different domain pass SID in url
- 				$session->SetMode(Session::smGET_ONLY);
- 			}
-		}
-
-		if (isset($params['opener']) && $params['opener'] == 'u') {
-			$wid = $this->GetVar('m_wid');
-			$stack_name = rtrim('opener_stack_'.$wid, '_');
-			$opener_stack = $this->RecallVar($stack_name);
-
-			if ($opener_stack && $opener_stack != serialize(Array())) {
-				$opener_stack = unserialize($opener_stack);
-				list($index_file, $env) = explode('|', $opener_stack[count($opener_stack) - 1]);
-				$ret = $this->BaseURL($prefix, $ssl).$index_file.'?'.ENV_VAR_NAME.'='.$env;
-				if ( getArrayValue($params,'escape') ) $ret = addslashes($ret);
-
-				if (isset($params['m_opener']) && $params['m_opener'] == 'u') {
-					array_pop($opener_stack);
-					if (!$opener_stack) {
-						$this->RemoveVar($stack_name);
-						// remove popups last templates, because popup is closing now
-						$this->RemoveVar('last_template_'.$wid);
-						$this->RemoveVar('last_template_popup_'.$wid);
-
-						// don't save popups last templates again :)
-						$this->SetVar('skip_last_template', 1);
-					}
-					else {
-						$this->StoreVar($stack_name, serialize($opener_stack));
-					}
-
-					/*// store window relations
-					$window_relations = $this->RecallVar('window_relations');
-					$window_relations = $window_relations ? unserialize($window_relations) : Array ();
-					if (array_key_exists($wid, $window_relations)) {
-						unset($window_relations[$wid]);
-						$this->StoreVar('window_relations', serialize($window_relations));
-					}*/
-				}
-				return $ret;
-			}
-			else {
-				//define('DBG_REDIRECT', 1);
-				$t = $this->GetVar('t');
-			}
-		}
-
-		$pass = isset($params['pass']) ? $params['pass'] : '';
-
-		// pass events with url
-		$pass_events = false;
-		if( isset($params['pass_events']) )
-		{
-			$pass_events = $params['pass_events'];
-			unset($params['pass_events']);
-		}
-
-		$map_link = '';
-		if( isset($params['anchor']) )
-		{
-			$map_link = '#'.$params['anchor'];
-			unset($params['anchor']);
-		}
-
-		if ( isset($params['no_amp']) )
-		{
-			$params['__URLENCODE__'] = $params['no_amp'];
-			unset($params['no_amp']);
-		}
-
-		$no_rewrite = false;
-		if( isset($params['__NO_REWRITE__']) )
-		{
-			$no_rewrite = true;
-			unset($params['__NO_REWRITE__']);
-		}
-
-		$force_rewrite = false;
-		if( isset($params['__MOD_REWRITE__']) )
-		{
-			$force_rewrite = true;
-			unset($params['__MOD_REWRITE__']);
-		}
-
-		$force_no_sid = false;
-		if( isset($params['__NO_SID__']) )
-		{
-			$force_no_sid = true;
-			unset($params['__NO_SID__']);
-		}
-
-		// append pass through variables to each link to be build
-		$params = array_merge($this->getPassThroughVariables($params), $params);
-
-		if ($force_rewrite || ($this->RewriteURLs($ssl) && !$no_rewrite)) {
-			static $rewrite_listeners_done = false;
-
-			if (!$rewrite_listeners_done) {
-				$mod_rewrite_helper =& $this->recallObject('ModRewriteHelper');
-				/* @var $mod_rewrite_helper kModRewriteHelper */
-
-				$mod_rewrite_helper->initRewriteListeners();
-
-				$rewrite_listeners_done = true;
-			}
-
-			$session =& $this->recallObject('Session');
-
-			if ($session->NeedQueryString() && !$force_no_sid) {
-				$params['sid'] = $this->GetSID();
-			}
-
-			$url = $this->BuildEnv_NEW($t, $params, $pass, $pass_events);
-			$ret = $this->BaseURL($prefix, $ssl).$url.$map_link;
-		}
-		else {
-			unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off
-			$env = $this->BuildEnv($t, $params, $pass, $pass_events);
-			$ret = $this->BaseURL($prefix, $ssl).$index_file.'?'.$env.$map_link;
-		}
+		return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
+	}
 
-		return $ret;
+	function getPhysicalTemplate($template)
+	{
+		return $this->UrlManager->getPhysicalTemplate($template);
 	}
 
 	/**
 	 * Returns variables with values that should be passed throught with this link + variable list
 	 *
 	 * @param Array $params
 	 * @return Array
 	 */
 	function getPassThroughVariables(&$params)
 	{
-		static $cached_pass_through = null;
-
-		if (isset($params['no_pass_through']) && $params['no_pass_through']) {
-			unset($params['no_pass_through']);
-			return Array();
-		}
-
-		// because pass through is not changed during script run, then we can cache it
-		if (is_null($cached_pass_through)) {
-
-			$cached_pass_through = Array();
-			$pass_through = $this->GetVar('pass_through');
-
-			if ($pass_through) {
-				// names of variables to pass to each link
-				$cached_pass_through['pass_through'] = $pass_through;
-				$pass_through = explode(',', $pass_through);
-				foreach ($pass_through as $pass_through_var) {
-					$cached_pass_through[$pass_through_var] = $this->GetVar($pass_through_var);
-				}
-			}
-
-		}
-
-		return $cached_pass_through;
-	}
-
-
-	/**
-	 * Returns sorted array of passed prefixes (to build url from)
-	 *
-	 * @param string $pass
-	 * @return Array
-	 */
-	function getPassInfo($pass = 'all')
-	{
-		if (!$pass) $pass = 'all';
-		$pass = trim(
-				preg_replace(
-					'/(?<=,|\\A)all(?=,|\\z)/',
-					trim($this->GetVar('passed'), ','),
-					trim($pass, ',')
-				),
-		 ',');
-
-		if (!$pass) {
-			return Array();
-		}
-
-		$pass_info = array_unique( explode(',', $pass) ); // array( prefix[.special], prefix[.special] ...
-
-		// we need to keep that sorting despite the sorting below, because this sorts prefixes with same priority by name
-		sort($pass_info, SORT_STRING); // to be prefix1,prefix1.special1,prefix1.special2,prefix3.specialX
-
-		foreach ($pass_info as $prefix) {
-			list ($prefix_only, ) = explode('.', $prefix, 2);
-			$sorted[$prefix] = $this->getUnitOption($prefix_only, 'RewritePriority', 0);
-		}
-
-		asort($sorted, SORT_NUMERIC);
-		$pass_info = array_keys($sorted);
-
-		// ensure that "m" prefix is at the beginning
-		$main_index = array_search('m', $pass_info);
-		if ($main_index !== false) {
-			unset($pass_info[$main_index]);
-			array_unshift($pass_info, 'm');
-		}
-		return $pass_info;
-	}
-
-	function BuildEnv_NEW($t, $params, $pass='all', $pass_events = false)
-	{
-		if ($this->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->findModule('Var', $prefix) && $this->getUnitOption($prefix, 'CatalogItem');
-
-				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;
-						}
-					}
-
-					// rewrited url part
-					$url_part = $this->BuildModuleEnv_NEW($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->BuildModuleEnv($pass_element, $params, $pass_events);
-					}
-				}
-				else {
-					$env .= ':' . $this->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_NEW('m', $params, $pass_events) . '/' . $ret;
-			$cat_processed = array_key_exists('category_processed', $params) && $params['category_processed'];
-
-			// remove tempporary parameters used by listeners
-			unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']);
-
-			if (array_key_exists('url_ending', $params)) {
-				$ret = trim($ret, '/') . $params['url_ending'];
-				unset($params['url_ending']);
-			}
-			else {
-				$ret = trim($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));
-
-		$params_str = '';
-		$join_string = $encode ? '&' : '&amp;';
-
-		foreach ($params as $param => $value) {
-			$params_str .= $join_string . $param . '=' . $value;
-		}
-
-		if ($params_str) {
-			$ret .= '?' . substr($params_str, strlen($join_string));
-		}
-
-		if ($encode) {
-			$ret = str_replace('\\', '%5C', $ret);
-		}
-
-		return $ret;
-	}
-
-	function BuildModuleEnv_NEW($prefix_special, &$params, $keep_events = false)
-	{
-		list ($prefix) = explode('.', $prefix_special);
-
-		$url_parts = Array ();
-		$listener = $this->RewriteListeners[$prefix][0];
-
-		$ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $keep_events);
-
-		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
-	 */
-	function BuildModuleEnv($prefix_special, &$params, $pass_events = false)
-	{
-		list($prefix) = explode('.', $prefix_special);
-		$query_vars = $this->getUnitOption($prefix, 'QueryString');
-
-		//if pass events is off and event is not implicity passed
-		if( !$pass_events && !isset($params[$prefix_special.'_event']) ) {
-			$params[$prefix_special.'_event'] = ''; // remove event from url if requested
-			//otherwise it will use value from get_var
-		}
-
-		if(!$query_vars) return '';
-
-		$tmp_string = Array(0 => $prefix_special);
-		foreach($query_vars as $index => $var_name)
-		{
-			//if value passed in params use it, otherwise use current from application
-			$var_name = $prefix_special.'_'.$var_name;
-			$tmp_string[$index] =  isset( $params[$var_name] ) ? $params[$var_name] : $this->GetVar($var_name);
-			if ( isset($params[$var_name]) ) unset( $params[$var_name] );
-		}
-
-		$escaped = array();
-		foreach ($tmp_string as $tmp_val) {
-			$escaped[] = str_replace(Array('-',':'), Array('\-','\:'), $tmp_val);
-		}
-
-		$ret = implode('-', $escaped);
-		if ($this->getUnitOption($prefix, 'PortalStyleEnv') == true)
-		{
-			$ret = preg_replace('/^([a-zA-Z]+)-([0-9]+)-(.*)/','\\1\\2-\\3', $ret);
-		}
-		return $ret;
+		return $this->UrlManager->getPassThroughVariables($params);
 	}
 
 	function BuildEnv($t, $params, $pass='all', $pass_events = false, $env_var = true)
 	{
-		if ($this->GetVar('admin') || (array_key_exists('admin', $params) && $params['admin'])) {
-			$params['admin'] = 1;
-
-			if (!array_key_exists('editing_mode', $params)) {
-				$params['editing_mode'] = EDITING_MODE;
-			}
-		}
-
-		$session =& $this->recallObject('Session');
-		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : 0;
-		$sid = $session->NeedQueryString() && !$this->RewriteURLs($ssl) ? $this->GetSID() : '';
-//		if (getArrayValue($params,'admin') == 1) $sid = $this->GetSID();
-
-		$ret = '';
-		if ($env_var) {
-			$ret = ENV_VAR_NAME.'=';
-		}
-
-		$ret .=	$sid . '-'; // SID-TEMPLATE
-
-		$encode = false;
-		if (isset($params['__URLENCODE__'])) {
-			$encode = $params['__URLENCODE__'];
-			unset($params['__URLENCODE__']);
-		}
-
-		if (isset($params['__SSL__'])) {
-			unset($params['__SSL__']);
-		}
-
-		$env_string = '';
-		$category_id = isset($params['m_cat_id']) ? $params['m_cat_id'] : $this->GetVar('m_cat_id');
-
-		$item_id = false;
-		$pass_info = $this->getPassInfo($pass);
-		if ($pass_info) {
-			if ($pass_info[0] == 'm') array_shift($pass_info);
-			foreach ($pass_info as $pass_element) {
-				list($prefix) = explode('.', $pass_element);
-				$require_rewrite = $this->findModule('Var', $prefix);
-				if ($require_rewrite) {
-					$item_id = isset($params[$pass_element.'_id']) ? $params[$pass_element.'_id'] : $this->GetVar($pass_element.'_id');
-				}
-				$env_string .= ':'.$this->BuildModuleEnv($pass_element, $params, $pass_events);
-			}
-		}
-
-		if (strtolower($t) == '__default__') {
-			if (is_numeric($item_id)) {
-				$mod_rw_helper =& $this->recallObject('ModRewriteHelper');
-				/* @var $mod_rw_helper kModRewriteHelper */
-
-				$t = $mod_rw_helper->GetItemTemplate($category_id, $pass_element); // $pass_element should be the last processed element
-				// $t = $this->getCategoryCache($category_id, 'item_templates');
-			}
-			elseif ($category_id) {
-				$t = strtolower(preg_replace('/^Content\//i', '', $this->getCategoryCache($category_id, 'filenames') ));
-			}
-			else {
-				$t = 'index';
-			}
-		}
-
-		$ret .= $t.':'.$this->BuildModuleEnv('m', $params, $pass_events).$env_string;
-
-		unset($params['pass'], $params['opener'], $params['m_event']);
-
-		if (array_key_exists('escape', $params) && $params['escape']) {
-			$ret = addslashes($ret);
-			unset($params['escape']);
-		}
-
-		$join_string = $encode ? '&' : '&amp;';
-		$params_str = '';
-		foreach ($params as $param => $value)
-		{
-			$params_str .= $join_string.$param.'='.$value;
-		}
-		$ret .= $params_str;
-
-		if ($encode) {
-			$ret = str_replace('\\', '%5C', $ret);
-		}
-		return $ret;
+		return $this->UrlManager->BuildEnv($t, $params, $pass, $pass_events, $env_var);
 	}
 
 	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 : '') . rtrim(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 : '') . rtrim($this->ConfigValue('Site_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
 					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);
 				}
 			}
 		}
 
 		ob_end_flush();
 		// 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();
 		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');
 		$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.current');
 			$user->SetError('ValidateLogin', 'session_expired', 'la_text_sess_expired');
 		}
 
 		if (($user_id != USER_GUEST) && kUtil::constOn('DBG_REQUREST_LOG') ) {
 			$http_query =& $this->recallObject('HTTPQuery');
 			$http_query->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();
 	}
 
-	function LoadCache()
-	{
-		// TODO: maybe language part isn't required, since same phrase from different languages have one ID now
-		$cache_key = $this->GetVar('t') . $this->GetVar('m_theme') . $this->GetVar('m_lang') . $this->isAdmin;
-
-		$sql = 'SELECT PhraseList, ConfigVariables
-				FROM ' . TABLE_PREFIX . 'PhraseCache
-				WHERE Template = ' . $this->Conn->qstr( md5($cache_key) );
-		$res = $this->Conn->GetRow($sql);
-
-		if ($res) {
-			$this->Caches['PhraseList'] = $res['PhraseList'] ? explode(',', $res['PhraseList']) : Array ();
-			$config_ids = $res['ConfigVariables'] ? explode(',', $res['ConfigVariables']) : Array ();
-
-			if (isset($this->Caches['ConfigVariables'])) {
-				$config_ids = array_diff($config_ids, $this->Caches['ConfigVariables']);
-			}
-		}
-		else {
-			$config_ids = Array ();
-		}
-
-		$this->Phrases->Init('phrases');
-		$this->Caches['ConfigVariables'] = $config_ids;
-		$this->ConfigCacheIds = $config_ids;
-	}
-
-	/**
-	 * Loads template mapping for Front-End
-	 *
-	 */
-	function LoadStructureTemplateMapping()
-	{
-		if (!$this->isAdmin) {
-			$category_helper =& $this->recallObject('CategoryHelper');
-			/* @var $category_helper CategoryHelper */
-
-			$this->structureTemplateMapping = $category_helper->getTemplateMapping();
-		}
-	}
-
-	function UpdateCache()
-	{
-		$update = false;
-		//something changed
-		$update = $update || $this->Phrases->NeedsCacheUpdate();
-		$update = $update || (count($this->ConfigCacheIds) && $this->ConfigCacheIds != $this->Caches['ConfigVariables']);
-
-		if ($update) {
-			$cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->isAdmin;
-			$query = sprintf("REPLACE %s (PhraseList, CacheDate, Template, ConfigVariables)
-												VALUES (%s, %s, %s, %s)",
-												TABLE_PREFIX.'PhraseCache',
-												$this->Conn->qstr(join(',', $this->Phrases->Ids)),
-												adodb_mktime(),
-												$this->Conn->qstr(md5($cache_key)),
-												$this->Conn->qstr(implode(',', array_unique($this->ConfigCacheIds))));
-			$this->Conn->Query($query);
-		}
-	}
-
-	function InitConfig()
-	{
-		if (isset($this->Caches['ConfigVariables']) && count($this->Caches['ConfigVariables']) > 0) {
-			$sql = 'SELECT VariableValue, VariableName
-					FROM ' . TABLE_PREFIX . 'ConfigurationValues
-				 	WHERE VariableId IN (' . implode(',', $this->Caches['ConfigVariables']) . ')';
-			$this->ConfigHash = array_merge($this->ConfigHash, $this->Conn->GetCol($sql, 'VariableName'));
-		}
-	}
-
 	/**
 	 * Returns configuration option value by name
 	 *
 	 * @param string $name
 	 * @return string
 	 */
 	function ConfigValue($name)
 	{
-		if ($name == 'Smtp_AdminMailFrom') {
-			$res = $this->siteDomainField('AdminEmail');
-
-			if ($res) {
-				return $res;
-			}
-		}
-
-		$res = array_key_exists($name, $this->ConfigHash) ? $this->ConfigHash[$name] : false;
-		if ($res !== false) {
-			return $res;
-		}
-
-		if (defined('IS_INSTALL') && IS_INSTALL && !$this->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->ConfigHash[$name] = $res['VariableValue'];
-			$this->ConfigCacheIds[] = $res['VariableId'];
-
-			return $res['VariableValue'];
-		}
-
-		return false;
-	}
-
-	function UpdateConfigCache()
-	{
-		if ($this->ConfigCacheIds) {
-
-		}
+		return $this->cacheManager->ConfigValue($name);
 	}
 
 	/**
 	 * 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);
 		}
 
 		if ( !isset($this->EventManager) ) {
 			$this->EventManager =& $this->recallObject('EventManager');
 		}
 
 		$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);
 	}
 
 	/**
 	 * Registers Hook from subprefix event to master prefix event
 	 *
 	 * @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);
 	}
 
 	/**
 	 * Allows one TagProcessor tag act as other TagProcessor tag
 	 *
 	 * @param Array $tag_info
 	 * @author Kostja
 	 */
 	function registerAggregateTag($tag_info)
 	{
 		$aggregator =& $this->recallObject('TagsAggregator', 'kArray');
 		$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], Array($tag_info['LocalPrefix'], $tag_info['LocalTagName'], getArrayValue($tag_info, 'LocalSpecial')));
 	}
 
 	/**
 	 * 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
 	 */
-	function &recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
+	public function &recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
 	{
 		$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
 	 */
-	function removeObject($name)
+	public function removeObject($name)
 	{
 		$this->Factory->DestroyObject($name);
 	}
 
 	/**
 	 * Get's real class name for pseudo class, includes class file and creates class instance
 	 *
 	 * @param string $pseudo_class
 	 * @param Array $arguments
 	 * @return kBase
 	 * @access public
 	 */
-	function &makeClass($pseudo_class, $arguments = Array ())
+	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
 	 */
-	function isDebugMode($check_debugger = true)
+	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 $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
-	 * @author Alex
 	 */
-	function getUnitOption($prefix, $option, $default = false)
+	public function getUnitOption($prefix, $option, $default = false)
 	{
-		/*if (!isset($this->UnitConfigReader)) {
-			$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
-		}*/
 		return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
 	}
 
 	/**
 	 * Set's new unit option value
 	 *
 	 * @param string $prefix
 	 * @param string $name
 	 * @param string $value
-	 * @author Alex
 	 * @access public
 	 */
-	function setUnitOption($prefix, $option, $value)
+	public function setUnitOption($prefix, $option, $value)
 	{
-//		$unit_config_reader =& $this->recallObject('kUnitConfigReader');
 		return $this->UnitConfigReader->setUnitOption($prefix,$option,$value);
 	}
 
 	/**
 	 * Read all unit with $prefix options
 	 *
 	 * @param string $prefix
 	 * @return Array
 	 * @access public
-	 * @author Alex
 	 */
-	function getUnitOptions($prefix)
+	public function getUnitOptions($prefix)
 	{
-//		$unit_config_reader =& $this->recallObject('kUnitConfigReader');
 		return $this->UnitConfigReader->getUnitOptions($prefix);
 	}
 
 	/**
 	 * Returns true if config exists and is allowed for reading
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
-	function prefixRegistred($prefix)
+	public function prefixRegistred($prefix)
 	{
-		/*if (!isset($this->UnitConfigReader)) {
-			$this->UnitConfigReader =& $this->recallObject('kUnitConfigReader');
-		}*/
 		return $this->UnitConfigReader->prefixRegistred($prefix);
 	}
 
 	/**
 	 * Splits any mixing of prefix and
 	 * special into correct ones
 	 *
 	 * @param string $prefix_special
 	 * @return Array
 	 * @access public
-	 * @author Alex
 	 */
-	function processPrefix($prefix_special)
+	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
 	 */
 	function handleError($errno, $errstr = '', $errfile = '', $errline = '', $errcontext = '')
 	{
 		$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;
 
 		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
 	 */
 	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;
 
 		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;
 	}
 
 	protected function errorLogSilent($errno, $errstr = '', $errfile = '', $errline = '')
 	{
 		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(FULL_PATH . '/silent_log.txt', 'a');
 			fwrite($fp, '[' . $time . '] #' . $errno . ': ' . strip_tags($errstr) . ' in [' . $errfile . '] on line ' . $errline . "\n");
 			fclose($fp);
 		}
 	}
 
 	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;
 	}
 
 	/**
 	 * 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 subtable prefix passes
 	 * OR prefix, that has been found in REQUEST and some how is parent of passed subtable prefix
 	 *
 	 * @param string $current_prefix
 	 * @param string $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
 	 * @return string
 	 * @access public
 	 * @author Kostja / Alex
 	 */
 	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
 	 * @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, whitch are affected by one of specified tables
 	 *
 	 * @param string $tables comma separated tables list used in counting sqls
 	 */
 	function resetCounters($tables)
 	{
 		if (kUtil::constOn('IS_INSTALL')) {
 			return ;
 		}
 
 		$count_helper =& $this->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		return $count_helper->resetCounters($tables);
 	}
 
 	/**
 	 * Sends XML header + optionally displays xml heading
 	 *
 	 * @param string $xml_version
 	 * @return string
 	 * @author Alex
 	 */
 	function XMLHeader($xml_version = false)
 	{
 		$lang =& $this->recallObject('lang.current');
 		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)
+	{
+		$this->cacheManager->DeleteUnitCache($include_sections);
+	}
+
 	/**
 	 * Sets data from cache to object
 	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
-		$this->ConfigHash = $data['Application.ConfigHash'];
-
-		$this->Caches['ConfigVariables'] = $data['Application.ConfigCacheIds']; // undefined attribute
-		$this->ConfigCacheIds = $data['Application.ConfigCacheIds'];
-
 		$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.ConfigHash' => $this->ConfigHash,
-
-			'Application.Caches.ConfigVariables' => $this->Caches['ConfigVariables'], // undefined attribute
-			'Application.ConfigCacheIds' => $this->ConfigCacheIds,
-
-			'Application.ReplacementTemplates' => $this->ReplacementTemplates,
-			'Application.RewriteListeners' => $this->RewriteListeners,
-
-			'Application.ModuleInfo' => $this->ModuleInfo,
-		);
+		return 	Array (
+					'Application.ReplacementTemplates' => $this->ReplacementTemplates,
+					'Application.RewriteListeners' => $this->RewriteListeners,
+					'Application.ModuleInfo' => $this->ModuleInfo,
+				);
 	}
-
 }
\ No newline at end of file
Index: branches/5.2.x/core/kernel/managers/url_manager.php
===================================================================
--- branches/5.2.x/core/kernel/managers/url_manager.php	(nonexistent)
+++ branches/5.2.x/core/kernel/managers/url_manager.php	(revision 14184)
@@ -0,0 +1,631 @@
+<?php
+/**
+* @version	$Id$
+* @package	In-Portal
+* @copyright	Copyright (C) 1997 - 2010 Intechnic. All rights reserved.
+* @license      GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class kUrlManager extends kBase {
+
+	/**
+	 * Physical template name mapping to their template names based on structure
+	 *
+	 * @var Array
+	 */
+	protected $structureTemplateMapping = Array ();
+
+	/**
+	* Return href for template
+	*
+	* @access public
+	* @param string $t Template path
+	* @var string $prefix index.php prefix - could be blank, 'admin'
+	*/
+	public function HREF($t, $prefix = '', $params = null, $index_file = null)
+	{
+		static $theme_id = null;
+
+		if (!isset($theme_id)) {
+			$theme_id = $this->Application->GetVar('m_theme');
+		}
+
+		if (!$t) {
+			// when template not specified, use current
+			$t = $this->Application->GetVar('t');
+		}
+
+		$t = preg_replace('/^Content\//i', '', $t);
+
+		if (substr($t, -4) == '.tpl') {
+			// cut template extension (deprecated link format)
+			$t = substr($t, 0, strlen($t) - 4);
+		}
+
+		if (substr($t, 0, 3) == 'id:') {
+			// link to structure page using it's id
+			$params['m_cat_id'] = substr($t, 3);
+			$t = $this->structureTemplateMapping[$t];
+		}
+
+		if (array_key_exists('use_section', $params)) {
+			$use_section = $params['use_section'];
+			unset($params['use_section']);
+		}
+
+		if (isset($use_section) && $use_section && array_key_exists($t . ':' . $theme_id, $this->structureTemplateMapping)) {
+			// structure template corresponding to given physical template
+			$t = $this->structureTemplateMapping[$t . ':' . $theme_id];
+			unset($params['use_section']);
+		}
+
+		if (preg_match('/external:(.*)/', $t, $rets)) {
+			// external url
+			return $rets[1];
+		}
+
+		if ($this->Application->isAdmin && $prefix == '') $prefix = ADMIN_DIRECTORY;
+		if ($this->Application->isAdmin && $prefix == '_FRONT_END_') $prefix = '';
+
+		$index_file = $this->getIndexFile($prefix, $index_file, $params);
+
+		if (isset($params['_auto_prefix_'])) {
+			unset($params['_auto_prefix_']); // this is parser-related param, do not need to pass it here
+		}
+
+		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : null;
+		if ($ssl !== null) {
+			$session =& $this->Application->recallObject('Session');
+			/* @var $session Session */
+
+			$target_url = rtrim($this->Application->BaseURL('', $ssl, false), '/');
+			$cookie_url = trim($session->CookieDomain . $session->CookiePath, '/.');
+
+			// set session to GET_ONLY, to pass sid only if sid is REAL AND session is set
+			if (!preg_match('#' . preg_quote($cookie_url) . '#', $target_url) && $session->SessionSet) {
+				// when SSL<->NON-SSL redirect to different domain pass SID in url
+ 				$session->SetMode(Session::smGET_ONLY);
+ 			}
+		}
+
+		if (isset($params['opener']) && $params['opener'] == 'u') {
+			$wid = $this->Application->GetVar('m_wid');
+			$stack_name = rtrim('opener_stack_'.$wid, '_');
+			$opener_stack = $this->Application->RecallVar($stack_name);
+
+			if ($opener_stack && $opener_stack != serialize(Array())) {
+				$opener_stack = unserialize($opener_stack);
+				list($index_file, $env) = explode('|', $opener_stack[count($opener_stack) - 1]);
+				$ret = $this->Application->BaseURL($prefix, $ssl).$index_file.'?'.ENV_VAR_NAME.'='.$env;
+				if ( getArrayValue($params,'escape') ) $ret = addslashes($ret);
+
+				if (isset($params['m_opener']) && $params['m_opener'] == 'u') {
+					array_pop($opener_stack);
+					if (!$opener_stack) {
+						$this->Application->RemoveVar($stack_name);
+						// remove popups last templates, because popup is closing now
+						$this->Application->RemoveVar('last_template_'.$wid);
+						$this->Application->RemoveVar('last_template_popup_'.$wid);
+
+						// don't save popups last templates again :)
+						$this->Application->SetVar('skip_last_template', 1);
+					}
+					else {
+						$this->Application->StoreVar($stack_name, serialize($opener_stack));
+					}
+
+					/*// store window relations
+					$window_relations = $this->Application->RecallVar('window_relations');
+					$window_relations = $window_relations ? unserialize($window_relations) : Array ();
+					if (array_key_exists($wid, $window_relations)) {
+						unset($window_relations[$wid]);
+						$this->Application->StoreVar('window_relations', serialize($window_relations));
+					}*/
+				}
+				return $ret;
+			}
+			else {
+				//define('DBG_REDIRECT', 1);
+				$t = $this->Application->GetVar('t');
+			}
+		}
+
+		$pass = isset($params['pass']) ? $params['pass'] : '';
+
+		// pass events with url
+		$pass_events = false;
+		if( isset($params['pass_events']) )
+		{
+			$pass_events = $params['pass_events'];
+			unset($params['pass_events']);
+		}
+
+		$map_link = '';
+		if( isset($params['anchor']) )
+		{
+			$map_link = '#'.$params['anchor'];
+			unset($params['anchor']);
+		}
+
+		if ( isset($params['no_amp']) )
+		{
+			$params['__URLENCODE__'] = $params['no_amp'];
+			unset($params['no_amp']);
+		}
+
+		$no_rewrite = false;
+		if( isset($params['__NO_REWRITE__']) )
+		{
+			$no_rewrite = true;
+			unset($params['__NO_REWRITE__']);
+		}
+
+		$force_rewrite = false;
+		if( isset($params['__MOD_REWRITE__']) )
+		{
+			$force_rewrite = true;
+			unset($params['__MOD_REWRITE__']);
+		}
+
+		$force_no_sid = false;
+		if( isset($params['__NO_SID__']) )
+		{
+			$force_no_sid = true;
+			unset($params['__NO_SID__']);
+		}
+
+		// append pass through variables to each link to be build
+		$params = array_merge($this->getPassThroughVariables($params), $params);
+
+		if ($force_rewrite || ($this->Application->RewriteURLs($ssl) && !$no_rewrite)) {
+			static $rewrite_listeners_done = false;
+
+			if (!$rewrite_listeners_done) {
+				$mod_rewrite_helper =& $this->Application->recallObject('ModRewriteHelper');
+				/* @var $mod_rewrite_helper kModRewriteHelper */
+
+				$mod_rewrite_helper->initRewriteListeners();
+
+				$rewrite_listeners_done = true;
+			}
+
+			$session =& $this->Application->recallObject('Session');
+
+			if ($session->NeedQueryString() && !$force_no_sid) {
+				$params['sid'] = $this->Application->GetSID();
+			}
+
+			$url = $this->BuildEnv_NEW($t, $params, $pass, $pass_events);
+			$ret = $this->Application->BaseURL($prefix, $ssl).$url.$map_link;
+		}
+		else {
+			unset($params['pass_category']); // we don't need to pass it when mod_rewrite is off
+			$env = $this->BuildEnv($t, $params, $pass, $pass_events);
+			$ret = $this->Application->BaseURL($prefix, $ssl).$index_file.'?'.$env.$map_link;
+		}
+
+		return $ret;
+	}
+
+	/**
+	 * Returns variables with values that should be passed throught with this link + variable list
+	 *
+	 * @param Array $params
+	 * @return Array
+	 */
+	function getPassThroughVariables(&$params)
+	{
+		static $cached_pass_through = null;
+
+		if (isset($params['no_pass_through']) && $params['no_pass_through']) {
+			unset($params['no_pass_through']);
+			return Array();
+		}
+
+		// because pass through is not changed during script run, then we can cache it
+		if (is_null($cached_pass_through)) {
+
+			$cached_pass_through = Array();
+			$pass_through = $this->Application->GetVar('pass_through');
+
+			if ($pass_through) {
+				// names of variables to pass to each link
+				$cached_pass_through['pass_through'] = $pass_through;
+				$pass_through = explode(',', $pass_through);
+				foreach ($pass_through as $pass_through_var) {
+					$cached_pass_through[$pass_through_var] = $this->Application->GetVar($pass_through_var);
+				}
+			}
+
+		}
+
+		return $cached_pass_through;
+	}
+
+	function BuildEnv($t, $params, $pass='all', $pass_events = false, $env_var = true)
+	{
+		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;
+			}
+		}
+
+		$session =& $this->Application->recallObject('Session');
+		$ssl = isset($params['__SSL__']) ? $params['__SSL__'] : 0;
+		$sid = $session->NeedQueryString() && !$this->Application->RewriteURLs($ssl) ? $this->Application->GetSID() : '';
+//		if (getArrayValue($params,'admin') == 1) $sid = $this->Application->GetSID();
+
+		$ret = '';
+		if ($env_var) {
+			$ret = ENV_VAR_NAME.'=';
+		}
+
+		$ret .=	$sid . '-'; // SID-TEMPLATE
+
+		$encode = false;
+		if (isset($params['__URLENCODE__'])) {
+			$encode = $params['__URLENCODE__'];
+			unset($params['__URLENCODE__']);
+		}
+
+		if (isset($params['__SSL__'])) {
+			unset($params['__SSL__']);
+		}
+
+		$env_string = '';
+		$category_id = isset($params['m_cat_id']) ? $params['m_cat_id'] : $this->Application->GetVar('m_cat_id');
+
+		$item_id = false;
+		$pass_info = $this->getPassInfo($pass);
+		if ($pass_info) {
+			if ($pass_info[0] == 'm') array_shift($pass_info);
+			foreach ($pass_info as $pass_element) {
+				list($prefix) = explode('.', $pass_element);
+				$require_rewrite = $this->Application->findModule('Var', $prefix);
+				if ($require_rewrite) {
+					$item_id = isset($params[$pass_element.'_id']) ? $params[$pass_element.'_id'] : $this->Application->GetVar($pass_element.'_id');
+				}
+				$env_string .= ':'.$this->BuildModuleEnv($pass_element, $params, $pass_events);
+			}
+		}
+
+		if (strtolower($t) == '__default__') {
+			if (is_numeric($item_id)) {
+				$mod_rw_helper =& $this->Application->recallObject('ModRewriteHelper');
+				/* @var $mod_rw_helper kModRewriteHelper */
+
+				$t = $mod_rw_helper->GetItemTemplate($category_id, $pass_element); // $pass_element should be the last processed element
+				// $t = $this->Application->getCategoryCache($category_id, 'item_templates');
+			}
+			elseif ($category_id) {
+				$t = strtolower(preg_replace('/^Content\//i', '', $this->Application->getCategoryCache($category_id, 'filenames') ));
+			}
+			else {
+				$t = 'index';
+			}
+		}
+
+		$ret .= $t.':'.$this->BuildModuleEnv('m', $params, $pass_events).$env_string;
+
+		unset($params['pass'], $params['opener'], $params['m_event']);
+
+		if (array_key_exists('escape', $params) && $params['escape']) {
+			$ret = addslashes($ret);
+			unset($params['escape']);
+		}
+
+		if ($params) {
+			$params_str = '';
+			$join_string = $encode ? '&' : '&amp;';
+
+			foreach ($params as $param => $value) {
+				$params_str .= $join_string . $param . '=' . $value;
+			}
+
+			$ret .= $params_str;
+		}
+
+		if ($encode) {
+			$ret = str_replace('\\', '%5C', $ret);
+		}
+
+		return $ret;
+	}
+
+	function BuildEnv_NEW($t, $params, $pass='all', $pass_events = 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 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;
+						}
+					}
+
+					// rewrited url part
+					$url_part = $this->BuildModuleEnv_NEW($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->BuildModuleEnv($pass_element, $params, $pass_events);
+					}
+				}
+				else {
+					$env .= ':' . $this->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_NEW('m', $params, $pass_events) . '/' . $ret;
+			$cat_processed = array_key_exists('category_processed', $params) && $params['category_processed'];
+
+			// remove tempporary parameters used by listeners
+			unset($params['t'], $params['inject_parts'], $params['pass_template'], $params['pass_category'], $params['category_processed']);
+
+			if (array_key_exists('url_ending', $params)) {
+				$ret = trim($ret, '/') . $params['url_ending'];
+				unset($params['url_ending']);
+			}
+			else {
+				$ret = trim($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
+	 */
+	function BuildModuleEnv($prefix_special, &$params, $pass_events = false)
+	{
+		list($prefix) = explode('.', $prefix_special);
+		$query_vars = $this->Application->getUnitOption($prefix, 'QueryString');
+
+		//if pass events is off and event is not implicity passed
+		if( !$pass_events && !isset($params[$prefix_special.'_event']) ) {
+			$params[$prefix_special.'_event'] = ''; // remove event from url if requested
+			//otherwise it will use value from get_var
+		}
+
+		if(!$query_vars) return '';
+
+		$tmp_string = Array(0 => $prefix_special);
+		foreach($query_vars as $index => $var_name)
+		{
+			//if value passed in params use it, otherwise use current from application
+			$var_name = $prefix_special.'_'.$var_name;
+			$tmp_string[$index] =  isset( $params[$var_name] ) ? $params[$var_name] : $this->Application->GetVar($var_name);
+			if ( isset($params[$var_name]) ) unset( $params[$var_name] );
+		}
+
+		$escaped = array();
+		foreach ($tmp_string as $tmp_val) {
+			$escaped[] = str_replace(Array('-',':'), Array('\-','\:'), $tmp_val);
+		}
+
+		$ret = implode('-', $escaped);
+		if ($this->Application->getUnitOption($prefix, 'PortalStyleEnv') == true)
+		{
+			$ret = preg_replace('/^([a-zA-Z]+)-([0-9]+)-(.*)/','\\1\\2-\\3', $ret);
+		}
+		return $ret;
+	}
+
+	function BuildModuleEnv_NEW($prefix_special, &$params, $keep_events = false)
+	{
+		list ($prefix) = explode('.', $prefix_special);
+
+		$url_parts = Array ();
+		$listener = $this->Application->RewriteListeners[$prefix][0];
+
+		$ret = $listener[0]->$listener[1](REWRITE_MODE_BUILD, $prefix_special, $params, $url_parts, $keep_events);
+
+		return $ret;
+	}
+
+	/**
+	 * Returns sorted array of passed prefixes (to build url from)
+	 *
+	 * @param string $pass
+	 * @return Array
+	 */
+	function getPassInfo($pass = 'all')
+	{
+		if (!$pass) $pass = 'all';
+		$pass = trim(
+				preg_replace(
+					'/(?<=,|\\A)all(?=,|\\z)/',
+					trim($this->Application->GetVar('passed'), ','),
+					trim($pass, ',')
+				),
+		 ',');
+
+		if (!$pass) {
+			return Array();
+		}
+
+		$pass_info = array_unique( explode(',', $pass) ); // array( prefix[.special], prefix[.special] ...
+
+		// we need to keep that sorting despite the sorting below, because this sorts prefixes with same priority by name
+		sort($pass_info, SORT_STRING); // to be prefix1,prefix1.special1,prefix1.special2,prefix3.specialX
+
+		foreach ($pass_info as $prefix) {
+			list ($prefix_only, ) = explode('.', $prefix, 2);
+			$sorted[$prefix] = $this->Application->getUnitOption($prefix_only, 'RewritePriority', 0);
+		}
+
+		asort($sorted, SORT_NUMERIC);
+		$pass_info = array_keys($sorted);
+
+		// ensure that "m" prefix is at the beginning
+		$main_index = array_search('m', $pass_info);
+		if ($main_index !== false) {
+			unset($pass_info[$main_index]);
+			array_unshift($pass_info, 'm');
+		}
+
+		return $pass_info;
+	}
+
+	/**
+	 * Returns index file, that could be passed as parameter to method, as parameter to tag and as constant or not passed at all
+	 *
+	 * @param string $prefix
+	 * @param string $index_file
+	 * @param Array $params
+	 * @return string
+	 */
+	function getIndexFile($prefix, $index_file, &$params)
+	{
+		if (isset($params['index_file'])) {
+			$index_file = $params['index_file'];
+			unset($params['index_file']);
+			return $index_file;
+		}
+
+		if (isset($index_file)) {
+			return $index_file;
+		}
+
+		if (defined('INDEX_FILE')) {
+			return INDEX_FILE;
+		}
+
+		$cut_prefix = trim(BASE_PATH, '/').'/'.trim($prefix, '/');
+		return trim(preg_replace('/'.preg_quote($cut_prefix, '/').'(.*)/', '\\1', $_SERVER['PHP_SELF']), '/');
+	}
+
+	function getPhysicalTemplate($template)
+	{
+		return array_search($template, $this->structureTemplateMapping);
+	}
+
+	/**
+	 * Loads template mapping for Front-End
+	 *
+	 */
+	function LoadStructureTemplateMapping()
+	{
+		if (!$this->Application->isAdmin) {
+			$category_helper =& $this->Application->recallObject('CategoryHelper');
+			/* @var $category_helper CategoryHelper */
+
+			$this->structureTemplateMapping = $category_helper->getTemplateMapping();
+		}
+	}
+}
\ 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	(nonexistent)
+++ branches/5.2.x/core/kernel/managers/cache_manager.php	(revision 14184)
@@ -0,0 +1,574 @@
+<?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();
+
+	/**
+	 * IDs of config variables used in current run (for caching)
+	 *
+	 * @var Array
+	 * @access protected
+	 */
+	protected $configIDs = Array ();
+
+	/**
+	 * IDs of config variables retirieved from cache
+	 *
+	 * @var Array
+	 * @access protected
+	 */
+	protected $originalConfigIDs = Array ();
+
+	/**
+	 * Object of memory caching class
+	 *
+	 * @var kCache
+	 * @access protected
+	 */
+	protected $cacheHandler = null;
+
+	/**
+	 * 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'];
+		}
+
+		return false;
+	}
+
+	/**
+	 * 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);
+		}
+		else {
+			$data = $this->Application->getDBCache('configs_parsed');
+		}
+
+		if ($data) {
+			$cache = unserialize($data); // 126 KB all modules
+			unset($data);
+
+			$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;
+
+		}
+
+		return false;
+	}
+
+	/**
+	 * 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);
+		}
+	}
+
+	/**
+	 * Deletes all data, that was cached during unit config parsing (including unit config locations)
+	 *
+	 * @param bool $include_sections
+	 * @access public
+	 */
+	public function DeleteUnitCache($include_sections = false)
+	{
+		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
+			$this->Application->deleteCache('master:configs_parsed');
+		}
+		else {
+			$this->Application->deleteDBCache('configs_parsed');
+		}
+
+		if ($include_sections) {
+			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
+				$this->Application->deleteCache('master:sections_parsed');
+			}
+			else {
+				$this->Application->deleteDBCache('sections_parsed');
+			}
+		}
+	}
+
+	/**
+	 * Preloads 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',
+
+			// output related
+			'UseModRewrite',
+			'UseContentLanguageNegotiation',
+			'UseOutputCompression',
+			'OutputCompressionLevel',
+			'Config_Site_Time',
+			'SystemTagCache',
+
+			// tracking related
+			'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
+	 *
+	 * @param Array $data
+	 * @access public
+	 */
+	public function setFromCache(&$data)
+	{
+		$this->configVariables = $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)
+	 *
+	 * @return int
+	 * @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
+	 * @return mixed
+	 * @access public
+	 */
+	public function getCache($key, $store_locally = true)
+	{
+		return $this->cacheHandler->getCache($key, $store_locally);
+	}
+
+	/**
+	 * 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->cacheHandler->setCache($key, $value, $expiration);
+	}
+
+	/**
+	 * 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
+	 * @return mixed
+	 * @access public
+	 */
+	public 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 $expiration
+	 * @access public
+	 */
+	public 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');
+	}
+
+	/**
+	 * 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/utility/formatters/password_formatter.php
===================================================================
--- branches/5.2.x/core/kernel/utility/formatters/password_formatter.php	(revision 14183)
+++ branches/5.2.x/core/kernel/utility/formatters/password_formatter.php	(revision 14184)
@@ -1,143 +1,145 @@
 <?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.
 */
 
 class kPasswordFormatter extends kFormatter
 {
 	/**
 	 * The method is supposed to alter config options or cofigure object in some way based on its usage of formatters
 	 * The methods is called for every field with formatter defined when configuring item.
 	 * Could be used for adding additional VirtualFields to an object required by some special Formatter
 	 *
 	 * @param string $field_name
 	 * @param array $field_options
 	 * @param kDBBase $object
 	 */
 	function PrepareOptions($field_name, &$field_options, &$object)
 	{
 		if ( isset( $field_options['verify_field'] ) ) {
 			$add_fields = Array ();
 			$options = Array ('master_field' => $field_name, 'formatter' => 'kPasswordFormatter');
 
 			$copy_options = Array ('encryption_method', 'salt', 'required', 'skip_empty');
 			foreach ($copy_options as $copy_option) {
 				if (array_key_exists($copy_option, $field_options)) {
 					$options[$copy_option] = $field_options[$copy_option];
 				}
 			}
 
 			$add_fields[ $field_options['verify_field'] ] = $options;
 
 			$add_fields[$field_name.'_plain'] = Array('type'=>'string', 'error_field'=>$field_name);
 			$add_fields[ $field_options['verify_field'].'_plain' ] = Array('type'=>'string', 'error_field'=>$field_options['verify_field'] );
 
 			$virtual_fields = $object->getVirtualFields();
 			$add_fields = kUtil::array_merge_recursive($add_fields, $virtual_fields);
 			$object->setVirtualFields($add_fields);
 		}
 	}
 
 	function Format($value, $field_name, &$object, $format=null)
 	{
 		return $value;
 	}
 
 	/**
 	 * Performs password & verify password field validation
 	 *
 	 * @param mixed $value
 	 * @param string $field_name
 	 * @param kDBItem $object
 	 * @return string
 	 */
 	function Parse($value, $field_name, &$object)
 	{
 		$options = $object->GetFieldOptions($field_name);
 
 		$flip_count = 0;
 		$fields_set = true;
 		$fields = Array ('master_field', 'verify_field');
 
 		// 1. collect values from both Password and VerifyPassword fields
 		while ($flip_count < 2) {
 			if ( getArrayValue($options, $fields[0]) ) {
 				$tmp_field = $options[ $fields[0] ];
 				$object->SetDBField($field_name.'_plain', $value);
 
 				if ( !$object->GetFieldOption($tmp_field, $fields[1].'_set') ) {
 					$object->SetFieldOption($tmp_field, $fields[1].'_set', true);
 				}
 
 				$password_field = $options[ $fields[0] ];
 				$verify_field = $field_name;
 			}
 
 			$fields = array_reverse($fields);
 			$flip_count++;
 		}
 
 		$salt = $object->GetFieldOption($password_field, 'salt', false, '');
 
 		if ($object->GetFieldOption($password_field, 'verify_field_set') && $object->GetFieldOption($verify_field, 'master_field_set')) {
 			$new_password = $object->GetDBField($password_field . '_plain');
 			$verify_password = $object->GetDBField($verify_field . '_plain');
 
 			if ($new_password == '' && $verify_password == '') {
 				// both passwords are empty -> keep old password
 				if ($object->GetDBField($password_field) != $this->EncryptPassword('', $salt)) {
 					if ($options['encryption_method'] == 'plain') {
 						return $value;
 					}
 
 					return $this->EncryptPassword($value);
 				}
 				else {
 					return $value;
 				}
 			}
 
 			// determine admin or front
 			$phrase_error_prefix = $this->Application->isAdmin ? 'la' : 'lu';
 
 			if ($new_password != $verify_password) {
 				// passwords don't match (no matter what is their length)
 				$object->SetError($verify_field, 'passwords_do_not_match', $phrase_error_prefix.'_passwords_do_not_match');
 			}
 
 			$min_length = $this->Application->ConfigValue('Min_Password'); // for error message too
+			$min_length = $object->GetFieldOption($password_field, 'min_length', false, $min_length);
+
 			if (mb_strlen($new_password) < $min_length) {
 				$error_msg = '+' . sprintf($this->Application->Phrase($phrase_error_prefix.'_passwords_too_short'), $min_length); // + -> not phrase
 				$object->SetError($password_field, 'passwords_min_length', $error_msg);
 			}
 		}
 
 		if ($value == '') {
 			return $object->GetDBField($field_name);
 		}
 
 		if ($options['encryption_method'] == 'plain') {
 			return $value;
 		}
 
 		return $this->EncryptPassword($value, $salt);
 	}
 
 	function EncryptPassword($value, $salt=null)
 	{
 		if (!isset($salt) || !$salt) {
 			// if empty salt, assume, that it's not passed at all
 			return md5($value);
 		}
 		return md5(md5($value).$salt);
 	}
 }
\ 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 14183)
+++ branches/5.2.x/core/kernel/utility/unit_config_reader.php	(revision 14184)
@@ -1,1147 +1,1046 @@
 <?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 readed
 	 *
 	 * @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 CacheParsedData()
-	{
-		$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
-		/* @var $aggregator kArray */
-
-		$this->preloadConfigVars(); // preloading will put to cache
-
-		$cache = array_merge(
-			$this->Application->Factory->getToCache(),
-			$this->getToCache(),
-			$this->Application->EventManager->getToCache(),
-			$aggregator->getToCache(),
-			$this->Application->getToCache()
-		);
-
-		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-			$this->Application->setCache('master:configs_parsed', serialize($cache));
-			$this->Application->setCache('master:config_files', serialize($this->configFiles));
-		}
-		else {
-			$this->Application->setDBCache('configs_parsed', serialize($cache));
-			$this->Application->setDBCache('config_files', serialize($this->configFiles));
-		}
-
-		$cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s');
-		$this->Application->setDBCache('last_cache_rebuild', $cache_rebuild_by);
-
-		unset($this->configFiles);
-	}
-
-	function preloadConfigVars()
-	{
-		$config_vars = Array (
-			// session related
-			'SessionTimeout',
-			'SessionCookieName',
-			'SessionCookieDomains',
-			'SessionBrowserSignatureCheck',
-			'SessionIPAddressCheck',
-			'CookieSessions',
-			'KeepSessionOnBrowserClose',
-			'User_GuestGroup',
-			'User_LoggedInGroup',
-
-			// output related
-			'UseModRewrite',
-			'UseContentLanguageNegotiation',
-			'UseOutputCompression',
-			'OutputCompressionLevel',
-			'Config_Site_Time',
-			'SystemTagCache',
-
-			// tracking related
-			'UseChangeLog',
-			'UseVisitorTracking',
-			'ModRewriteUrlEnding',
-			'ForceModRewriteUrlEnding',
-			'UseCronForRegularEvent',
-		);
-
-		foreach ($config_vars as $var) {
-			$this->Application->ConfigValue($var);
-		}
-	}
-
-	function RestoreParsedData()
-	{
-		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-			$data = $this->Application->getCache('master:configs_parsed', false);
-		}
-		else {
-			$data = $this->Application->getDBCache('configs_parsed');
-		}
-
-		if ($data) {
-			$cache = unserialize($data); // 126 KB all modules
-			unset($data);
-
-			$this->Application->Factory->setFromCache($cache);
-			$this->setFromCache($cache);
-			$this->Application->EventManager->setFromCache($cache);
-
-			$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
-			/* @var $aggregator kArray */
-
-			$aggregator->setFromCache($cache);
-			$this->Application->setFromCache($cache);
-			unset($cache);
-
-			return true;
-
-		}
-
-		return false;
-	}
-
-	function ResetParsedData($include_sections = false)
-	{
-		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-			$this->Application->deleteCache('master:configs_parsed');
-		}
-		else {
-			$this->Application->deleteDBCache('configs_parsed');
-		}
-
-		if ($include_sections) {
-			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-				$this->Application->deleteCache('master:sections_parsed');
-			}
-			else {
-				$this->Application->deleteDBCache('sections_parsed');
-			}
-		}
-	}
-
 	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->RestoreParsedData();
+			$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 beacuse AfterConfigRead needs ability to change config data
 		$this->StoreCache = $cache;
 	}
 
 	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);
 		}
 		else {
 			$data = $this->Application->getDBCache('config_files');
 		}
 
-		if ($cache && $data) {
+		if ($data) {
 			$this->configFiles = unserialize($data);
 
 			if ( !defined('DBG_VALIDATE_CONFIGS') && !DBG_VALIDATE_CONFIGS ) {
 				shuffle($this->configFiles);
 			}
 		}
 		else {
 			$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)) {
+		if ( !isset($store_cache) ) {
 			// store cache not overrided -> 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->CacheParsedData();
+			$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()
 	{
 		// clear restored cache (not in db)
 		$this->Application->Factory->Files = Array ();
 		$this->Application->Factory->realClasses = Array ();
 		$this->Application->Factory->Dependencies = Array ();
 
 		$this->Application->EventManager->beforeRegularEvents = Array ();
 		$this->Application->EventManager->afterRegularEvents = Array ();
 		$this->Application->EventManager->beforeHooks = Array ();
 		$this->Application->EventManager->afterHooks = Array ();
 
 		// otherwise ModulesHelper indirectly used from includeConfigFiles won't work
 		$this->Application->RegisterDefaultClasses();
 
 		// 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);
 			}
 		}
 
 		// 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
 	 *
 	 */
 	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->EventManager->registerBuildEvent($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->EventManager->registerAgent( $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->registerHook($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->registerAggregateTag($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];
 		$deps = getArrayValue($config, '_Dependencies');
 
 		if (!$deps) {
 			return ;
 		}
 
 		foreach ($deps 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] : false; // getArrayValue($main_config, $config_key);
 		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_required_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;
 			}
 			elseif ($prefix = array_search($filename, $this->prefixFiles)) {
 				// 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/kernel/languages/phrases_cache.php
===================================================================
--- branches/5.2.x/core/kernel/languages/phrases_cache.php	(revision 14183)
+++ branches/5.2.x/core/kernel/languages/phrases_cache.php	(revision 14184)
@@ -1,383 +1,341 @@
 <?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 PhrasesCache extends kBase {
 
 	var $Phrases = Array();
 	var $Ids = Array();
 	var $OriginalIds = Array(); //for comparing cache
 
 	var $LanguageId = null;
 
 	/**
 	 * Administrator's language, when visiting site (from frame)
 	 *
 	 * @var int
 	 */
 	var $AdminLanguageId = null;
 
 	var $fromTag = false;
 
 	/**
 	 * Allows to edit existing phrases
 	 *
 	 * @var bool
 	 */
 	var $_editExisting = false;
 
 	/**
 	 * Allows to edit missing phrases
 	 *
 	 * @var bool
 	 */
 	var $_editMissing = false;
 
 	/**
 	 * Template, used for phrase adding/editing
 	 *
 	 * @var string
 	 */
 	var $_phraseEditTemplate = 'languages/phrase_edit';
 
 	/**
 	 * Use simplified form for phrase editing
 	 *
 	 * @var bool
 	 */
 	var $_simpleEditingMode = false;
 
 	/**
 	 * HTML tag used to translate phrases
 	 *
 	 * @var string
 	 */
 	var $_translateHtmlTag = 'a';
 
 	/**
 	 * Phrases, that are in cache, but are not in database
 	 *
 	 * @var Array
 	 */
 	var $_missingPhrases = Array ();
 
 	/**
 	 * Mask for editing link
 	 *
 	 * @var string
 	 */
 	var $_editLinkMask = '';
 
 	/**
 	 * Escape phrase name, before placing it in javascript translation link
 	 * @var bool
 	 */
 	var $_escapePhraseName = true;
 
 	/**
 	 * Sets phrase editing mode, that corresponds current editing mode
 	 *
 	 */
 	function setPhraseEditing()
 	{
 		if (!$this->Application->isAdmin && (EDITING_MODE == EDITING_MODE_CONTENT)) {
 			// front-end viewed in content mode
 			$this->_editExisting = true;
 			$this->_editMissing = true;
 			$this->_simpleEditingMode = true;
 			$this->_translateHtmlTag = 'span';
 		}
 
 		$this->_editLinkMask = 'javascript:translate_phrase(\'#LABEL#\', \'' . $this->_phraseEditTemplate . '\', {event: \'OnPreparePhrase\', simple_mode: ' . ($this->_simpleEditingMode ? 'true' : 'false') . '});';
 
 		if (defined('DEBUG_MODE') && DEBUG_MODE && !$this->Application->GetVar('admin')) {
 			// admin and front-end while not viewed using content mode (via admin)
 			$this->_editMissing = defined('DBG_PHRASES') && DBG_PHRASES;
 
 			if (!$this->Application->isAdmin) {
 				$this->_phraseEditTemplate = 'phrases_edit';
 
 				$url_params = Array (
 					'm_opener' => 'd',
 					'phrases_label' => '#LABEL#',
 					'phrases_event' => 'OnPreparePhrase',
 					'pass' => 'm,phrases'
 				);
 
 				$this->_escapePhraseName = false;
 				$this->_editLinkMask = $this->Application->HREF($this->_phraseEditTemplate, '', $url_params);
 			}
 		}
 	}
 
 	/**
 	 * Loads phrases from current language
 	 * Method is called manually (not from kFactory class) too
 	 *
 	 * @param string $prefix
 	 * @param string $special
+	 * @param int $language_id
+	 * @param Array $phrase_ids
 	 */
-	public function Init($prefix, $special = '')
+	public function Init($prefix, $special = '', $language_id = null, $phrase_ids = null)
 	{
 		parent::Init($prefix, $special);
 
 		if (kUtil::constOn('IS_INSTALL')) {
 			$this->LanguageId = 1;
 		}
 		else {
-			if ($this->Application->isAdmin) {
-				$this->LanguageId = $this->Application->Session->GetField('Language');
-			}
-			else {
-				$this->LanguageId = $this->Application->GetVar('m_lang');
-
-				if ($this->Application->GetVar('admin')) {
-					$admin_session =& $this->Application->recallObject('Session.admin');
-					/* @var $admin_session Session */
-
-					$this->AdminLanguageId = $admin_session->GetField('Language');
+			if ( !isset($language_id) ) {
+				if ($this->Application->isAdmin) {
+					$language_id = $this->Application->Session->GetField('Language');
+				}
+				else {
+					$language_id = $this->Application->GetVar('m_lang');
 				}
 			}
-		}
 
-		if (isset($this->Application->Caches['PhraseList'])) {
-			$this->LoadPhrases( $this->Application->Caches['PhraseList'] );
-		}
-	}
+			$this->LanguageId = $language_id;
 
-	function GetCachedIds()
-	{
-		$cache_key = md5($this->Application->GetVar('t') . $this->Application->GetVar('m_theme') . $this->Application->GetVar('m_lang'));
-
-		$sql = 'SELECT PhraseList, ConfigVariables
-				FROM ' . TABLE_PREFIX . 'PhraseCache
-				WHERE Template = ' . $this->Conn->qstr($cache_key);
-		$res = $this->Conn->GetRow($sql);
+			if (!$this->Application->isAdmin && $this->Application->GetVar('admin')) {
+				$admin_session =& $this->Application->recallObject('Session.admin');
+				/* @var $admin_session Session */
 
-		if ($res && $res['ConfigVariables']) {
-			$this->Application->OriginalConfigCacheIds =  explode(',', $res['ConfigVariables']);
-			$this->Application->ConfigCacheIds = $this->Application->OriginalConfigCacheIds;
+				$this->AdminLanguageId = $admin_session->GetField('Language');
+			}
 		}
 
-		return ($res === false) ? Array() : explode(',', $res['PhraseList']);
+		$this->LoadPhrases($phrase_ids);
 	}
 
 	function LoadPhrases($ids)
 	{
 		if ( !is_array($ids) || !implode('', $ids) ) {
-			return;
+			return ;
 		}
 
 		$sql = 'SELECT l' . $this->LanguageId . '_Translation, PhraseKey
 				FROM ' . TABLE_PREFIX . 'Phrase
 				WHERE PhraseId IN (' . implode(',', $ids) . ') AND l' . $this->LanguageId . '_Translation IS NOT NULL';
 		$this->Phrases = $this->Conn->GetCol($sql, 'PhraseKey');
 
-		/*foreach($phrases as $phrase => $tanslation)
-		{
-			$this->AddCachedPhrase(mb_strtoupper($phrase), $tanslation);
-		}*/
-
-		$this->Ids = $ids;
-		$this->OriginalIds = $ids;
+		$this->Ids = $this->OriginalIds = $ids;
 	}
 
 	function AddCachedPhrase($label, $value, $allow_editing = true)
 	{
 		// uppercase phrase name for cases, when this method is called outside this class
 		$cache_key = ($allow_editing ? '' : 'NE:') . mb_strtoupper($label);
 
 		$this->Phrases[$cache_key] = $value;
 	}
 
 	function NeedsCacheUpdate()
 	{
 		return is_array($this->Ids) && count($this->Ids) > 0 && $this->Ids != $this->OriginalIds;
 	}
 
-	/**
-	 * Copy from Application->UpdateCache method
-	 *
-	 * @deprecated
-	 */
-	function UpdateCache()
-	{
-		$update = false;
-		//something changed
-		$update = $update || (is_array($this->Ids) && count($this->Ids) > 0 && $this->Ids != $this->OriginalIds);
-		$update = $update || (count($this->Application->ConfigCacheIds) && $this->Application->ConfigCacheIds != $this->Application->OriginalConfigCacheIds);
-		if ($update) {
-			$query = sprintf("REPLACE %s (PhraseList, CacheDate, Template, ConfigVariables)
-												VALUES (%s, %s, %s, %s)",
-												TABLE_PREFIX.'PhraseCache',
-												$this->Conn->Qstr(join(',', $this->Ids)),
-												adodb_mktime(),
-												$this->Conn->Qstr(md5($this->Application->GetVar('t').$this->Application->GetVar('m_theme').$this->Application->GetVar('m_lang'))),
-												$this->Conn->qstr(implode(',', array_unique($this->Application->ConfigCacheIds))));
-			$this->Conn->Query($query);
-		}
-	}
-
 	function GetPhrase($label, $allow_editing = true, $use_admin = false)
 	{
 		if (!isset($this->LanguageId)) {
 			//actually possible when custom field contains references to language labels and its being rebuilt in OnAfterConfigRead
 			//which is triggered by Sections rebuild, which in turn read all the configs and all of that happens BEFORE seeting the language...
 			return 'impossible case';
 		}
 
 		// cut exclamation marks - depricated form of passing phrase name from templates
 		$label = preg_replace('/^!(.*)!$/', '\\1', $label);
 
 		if (strlen($label) == 0) {
 			return '';
 		}
 
 		$original_label = $this->_escapePhraseName ? addslashes($label) : $label;
 		$label = mb_strtoupper($label);
 
 		$cache_key = ($allow_editing ? '' : 'NE:') . $label;
 
 		if (array_key_exists($cache_key, $this->Phrases)) {
 			$translated_label = $this->Phrases[$cache_key];
 
 			if ($this->_editExisting && $allow_editing && !array_key_exists($label, $this->_missingPhrases)) {
 				// option to change translation for Labels
 				$edit_url = 'javascript:translate_phrase(\'' . $original_label . '\', \'' . $this->_phraseEditTemplate . '\', {event: \'OnPreparePhrase\', simple_mode: ' . ($this->_simpleEditingMode ? 'true' : 'false') . '});';
 				$translated_label = '<' . $this->_translateHtmlTag . ' href="' . $edit_url . '" name="cms-translate-phrase" title="Edit translation">' . $translated_label . '</' . $this->_translateHtmlTag . '>';
 
 				if ($this->fromTag) {
 					$translated_label = $this->escapeTagReserved($translated_label);
 				}
 			}
 
 			return $translated_label;
 		}
 
 		$this->LoadPhraseByLabel($label, $original_label, $allow_editing, $use_admin);
 
 		return $this->GetPhrase($label, $allow_editing);
 	}
 
 	function LoadPhraseByLabel($label, $original_label, $allow_editing = true, $use_admin = false)
 	{
 		if (!$allow_editing && !$use_admin && !array_key_exists($label, $this->_missingPhrases) && array_key_exists($label, $this->Phrases)) {
 			// label is aready translated, but it's version without on the fly translation code is requested
 			$this->Phrases['NE:' . $label] = $this->Phrases[$label];
 
 			return true;
 		}
 
 		$language_id = $use_admin ? $this->AdminLanguageId : $this->LanguageId;
 
 		$sql = 'SELECT PhraseId, l' . $language_id . '_Translation
 				FROM ' . TABLE_PREFIX . 'Phrase
 				WHERE (PhraseKey = ' . $this->Conn->qstr($label) . ') AND (l' . $language_id . '_Translation IS NOT NULL)';
 		$res = $this->Conn->GetRow($sql);
 
 		if ($res === false || count($res) == 0) {
 			$translation = '!' . $label . '!';
 
 			if ($this->_editMissing && $allow_editing) {
 				$edit_url = str_replace('#LABEL#', $original_label, $this->_editLinkMask);
 				$translation = '<' . $this->_translateHtmlTag . ' href="' . $edit_url . '" name="cms-translate-phrase" title="Translate">!' . $label . '!</' . $this->_translateHtmlTag . '>';
 
 				if ($this->fromTag) {
 					$translation = $this->escapeTagReserved($translation);
 				}
 
 				$this->_missingPhrases[$label] = true; // add as key for faster accessing
 			}
 
 			// add it as already cached, as long as we dont need to cache not found phrase
 			$this->AddCachedPhrase($label, $translation, $allow_editing);
 
 			return false;
 		}
 
 		$cache_key = ($allow_editing ? '' : 'NE:') . $label;
 		$this->Phrases[$cache_key] = $res['l' . $language_id . '_Translation'];
 
 		array_push($this->Ids, $res['PhraseId']);
 		$this->Ids = array_unique($this->Ids); // just to make sure
 
 		return true;
 	}
 
 	/**
 	 * Sort params by name and then by length
 	 *
 	 * @param string $a
 	 * @param string $b
 	 * @return int
 	 * @access private
 	 */
 	function CmpParams($a, $b)
 	{
 		$a_len = mb_strlen($a);
 		$b_len = mb_strlen($b);
 		if ($a_len == $b_len) return 0;
 		return $a_len > $b_len ? -1 : 1;
 	}
 
 	/**
 	 * 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, $forse_escaping = null)
 	{
 		$this->fromTag = true;
 		if( isset($forse_escaping) ) {
 			$this->fromTag = $forse_escaping;
 		}
 
 		preg_match_all("(!(la|lu)[^!]+!)", $text, $res, PREG_PATTERN_ORDER);
 		$language_tags = $res[0];
 		uasort($language_tags, Array(&$this, 'CmpParams'));
 
 		$i = 0;
 		$values = Array();
 
 		foreach ($language_tags as $label) {
 			array_push($values, $this->GetPhrase($label) );
 			//array_push($values, $this->Application->Phrase($label) );
 			$language_tags[$i] = '/' . $language_tags[$i] . '/';
 			$i++;
 		}
 
 		$this->fromTag = false;
 
 		return preg_replace($language_tags, $values, $text);
 	}
 
 	/**
 	 * Escape chars in phrase translation, that could harm parser to process tag
 	 *
 	 * @param string $text
 	 * @return string
 	 * @access private
 	 */
 	function escapeTagReserved($text)
 	{
 		$reserved = Array('"',"'"); // =
 		$replacement = Array('\"',"\'"); // \=
 		return str_replace($reserved,$replacement,$text);
 	}
 
 }
\ No newline at end of file
Index: branches/5.2.x/core/kernel/globals.php
===================================================================
--- branches/5.2.x/core/kernel/globals.php	(revision 14183)
+++ branches/5.2.x/core/kernel/globals.php	(revision 14184)
@@ -1,717 +1,717 @@
 <?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 kUtil {
 
 //	const KG_TO_POUND = 2.20462262;
 
 	const POUND_TO_KG = 0.45359237;
 
 	/**
 	 * Similar to array_merge_recursive but keyed-valued are always overwritten.
 	 * Priority goes to the 2nd array.
 	 *
 	 * @param $paArray1 array
 	 * @param $paArray2 array
 	 * @return array
 	 * @access public
 	 */
 	public static function array_merge_recursive($paArray1, $paArray2)
 	{
 		if (!is_array($paArray1) or !is_array($paArray2)) {
 			return $paArray2;
 		}
 
 		foreach ($paArray2 AS $sKey2 => $sValue2) {
 			$paArray1[$sKey2] = isset($paArray1[$sKey2]) ? self::array_merge_recursive($paArray1[$sKey2], $sValue2) : $sValue2;
 		}
 
 		return $paArray1;
 	}
 
 	/**
 	 * Prepend a reference to an element to the beginning of an array.
 	 * Renumbers numeric keys, so $value is always inserted to $array[0]
 	 *
 	 * @param $array array
 	 * @param $value mixed
 	 * @return int
 	 * @access public
 	 */
 	public static function array_unshift_ref(&$array, &$value)
 	{
 		$return = array_unshift($array,'');
 		$array[0] =& $value;
 		return $return;
 	}
 
 	/**
 	 * Rename key in associative array, maintaining keys order
 	 *
 	 * @param Array $array Associative Array
 	 * @param mixed $old Old key name
 	 * @param mixed $new New key name
 	 * @access public
 	 */
 	public static function array_rename_key(&$array, $old, $new)
 	{
 		$new_array = Array ();
 
 		foreach ($array as $key => $val) {
 			$new_array[ $key == $old ? $new : $key] = $val;
 		}
 
 		$array = $new_array;
 	}
 
 	/**
 	 * Same as print_r, but outputs result on screen or in debugger report (when in debug mode)
 	 *
 	 * @param Array $data
 	 * @param string $label
 	 * @param bool $on_screen
 	 * @access public
 	 */
 	public static function print_r($data, $label = '', $on_screen = false)
 	{
 		$is_debug = false;
 		if (class_exists('kApplication') && !$on_screen) {
 			$application =& kApplication::Instance();
 			$is_debug = $application->isDebugMode();
 		}
 
 		if ($is_debug) {
 			if ($label) {
 				$application->Debugger->appendHTML('<strong>' . $label . '</strong>');
 			}
 
 			$application->Debugger->dumpVars($data);
 		}
 		else {
 			if ($label) {
 				echo '<strong>' . $label . '</strong><br/>';
 			}
 
 			echo '<pre>', print_r($data, true), '</pre>';
 		}
 	}
 
 	/**
 	 * Define constant if it was not already defined before
 	 *
 	 * @param string $const_name
 	 * @param string $const_value
 	 * @access public
 	 */
 	public static function safeDefine($const_name, $const_value)
 	{
 		if ( !defined($const_name) ) {
 			define($const_name, $const_value);
 		}
 	}
 
 	/**
 	 * Parses "/system/config.php" file and returns the result
 	 *
 	 * @param bool $parse_section
 	 * @return Array
 	 * @access public
 	 */
 	public static function parseConfig($parse_section = false)
 	{
 		$file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php';
 
 		if (!file_exists($file)) {
 			return Array ();
 		}
 
 		if (file_exists($file) && !is_readable($file)) {
 			die('Could Not Open Ini File');
 		}
 
 		$contents = file($file);
 
 		if ($contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n") {
 			// format of "config.php" file before 5.1.0 version
 			array_shift($contents);
 
 			return parse_ini_string(implode('', $contents), $parse_section);
 		}
 
 		$_CONFIG = Array ();
 		require($file);
 
 		if ($parse_section) {
 			return $_CONFIG;
 		}
 
 		$ret = Array ();
 
 		foreach ($_CONFIG as $section => $section_variables) {
 			$ret = array_merge($ret, $section_variables);
 		}
 
 		return $ret;
 	}
 
 	/**
 	 * Returns parsed variables from "config.php" file
 	 *
 	 * @return Array
 	 * @access public
 	 */
 	public static function getConfigVars()
 	{
 		static $vars = null;
 
 		if ( !isset($vars) ) {
 			$vars = self::parseConfig();
 		}
 
 		return $vars;
 	}
 
 	/**
 	 * Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set
 	 *
 	 * @param string $file
 	 * @access public
 	 */
 	public static function includeOnce($file)
 	{
 		global $debugger;
 
 		if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) {
 
 			if ( in_array($file, get_required_files()) ) {
 				return ;
 			}
 
 			global $debugger;
 
 			/*$debugger->IncludeLevel++;
 			$before_mem = memory_get_usage();*/
 
 			$debugger->ProfileStart('inc_'.crc32($file), $file);
 			include_once($file);
 			$debugger->ProfileFinish('inc_'.crc32($file));
 			$debugger->profilerAddTotal('includes', 'inc_'.crc32($file));
 
 			/*$used_mem = memory_get_usage() - $before_mem;
 			$debugger->IncludeLevel--;
 			$debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file);
 			$debugger->IncludesData['mem'][] = $used_mem;
 			$debugger->IncludesData['time'][] = $used_time;
 			$debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/
 		}
 		else {
 			include_once($file);
 		}
 	}
 
 	/**
 	 * Checks if given string is a serialized array
 	 *
 	 * @param string $string
 	 * @return bool
 	 * @access public
 	 */
 	public static function IsSerialized($string)
 	{
 		if ( is_array($string) ) {
 			return false;
 		}
 
 		return preg_match('/a:([\d]+):{/', $string);
 	}
 
 	/**
 	 * Generates password of given length
 	 *
 	 * @param int $length
 	 * @return string
 	 * @access public
 	 */
 	public static function generatePassword($length = 10)
 	{
 		$pass_length = $length;
 
 		$p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z');
 		$p2 = Array ('a','e','i','o','u');
 		$p3 = Array ('1','2','3','4','5','6','7','8','9');
 		$p4 = Array ('(','&',')',';','%');    // if you need real strong stuff
 
 		// how much elements in the array
 		// can be done with a array count but counting once here is faster
 
 		$s1 = 21;// this is the count of $p1
 		$s2 = 5; // this is the count of $p2
 		$s3 = 9; // this is the count of $p3
 		$s4 = 5; // this is the count of $p4
 
 		// possible readable combinations
 
 		$c1 = '121';    // will be like 'bab'
 		$c2 = '212';      // will be like 'aba'
 		$c3 = '12';      // will be like 'ab'
 		$c4 = '3';        // will be just a number '1 to 9'  if you dont like number delete the 3
 		//$c5 = '4';        // uncomment to active the strong stuff
 
 		$comb = '4'; // the amount of combinations you made above (and did not comment out)
 
 		for ($p = 0; $p < $pass_length;) {
 			mt_srand((double)microtime() * 1000000);
 			$strpart = mt_rand(1, $comb);
 
 			// checking if the stringpart is not the same as the previous one
 			if ($strpart != $previous) {
 				$pass_structure .= ${'c' . $strpart};
 
 				// shortcutting the loop a bit
 				$p = $p + mb_strlen(${'c' . $strpart});
 			}
 			$previous = $strpart;
 		}
 
 		// generating the password from the structure defined in $pass_structure
 		for ($g = 0; $g < mb_strlen($pass_structure); $g++) {
 			mt_srand((double)microtime() * 1000000);
 			$sel = mb_substr($pass_structure, $g, 1);
 			$pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ];
 		}
 
 		return $pass;
 	}
 
 	/**
 	 * Reverts effects of "htmlspecialchars" function
 	 *
 	 * @param string $string
 	 * @return string
 	 * @access public
 	 */
 	public static function unhtmlentities($string)
 	{
-		$trans_tbl = get_html_translation_table(HTML_ENTITIES);
+		$trans_tbl = get_html_translation_table(HTML_ENTITIES); // from PHP 5.3.4: , ENT_COMPAT, 'utf-8');
 		$trans_tbl = array_flip ($trans_tbl);
 
 		return strtr($string, $trans_tbl);
 	}
 
 	/**
 	 * submits $url with $post as POST
 	 *
 	 * @param string $url
 	 * @param mixed $data
 	 * @return string
 	 * @access public
 	 * @deprecated
 	 */
 	public static function curl_post($url, $data, $headers = null, $request_type = 'POST', $curl_options = null)
 	{
 		$application =& kApplication::Instance();
 
 		$curl_helper =& $application->recallObject('CurlHelper');
 		/* @var $curl_helper kCurlHelper */
 
 		if ($request_type == 'POST') {
 			$curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST);
 		}
 
 		$curl_helper->SetRequestData($data);
 
 		if (!is_null($headers)) {
 			// not an associative array, so don't use kCurlHelper::SetHeaders method
 			$curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) );
 		}
 
 		if (is_array($curl_options)) {
 			$curl_helper->setOptions($curl_options);
 		}
 
 		$curl_helper->followLocation = false;
 		$ret = $curl_helper->Send($url);
 
 		$GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode;
 		$GLOBALS['curl_error'] = $curl_helper->lastErrorMsg;
 
 		return $ret;
 	}
 
 	/**
 	 * Checks if constant is defined and has positive value
 	 *
 	 * @param string $const_name
 	 * @return bool
 	 * @access public
 	 */
 	public static function constOn($const_name)
 	{
 		return defined($const_name) && constant($const_name);
 	}
 
 	/**
 	 * Converts KG to Pounds
 	 *
 	 * @param float $kg
 	 * @param bool $pounds_only
 	 * @return float
 	 * @access public
 	 */
 	public static function Kg2Pounds($kg, $pounds_only = false)
 	{
 		$major = floor( round($kg / self::POUND_TO_KG, 3) );
 		$minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2));
 
 		if ($pounds_only) {
 			$major += round($minor * 0.0625, 2);
 			$minor = 0;
 		}
 		return array($major, $minor);
 	}
 
 	/**
 	 * Converts Pounds to KG
 	 *
 	 * @param float $pounds
 	 * @param float $ounces
 	 * @return float
 	 * @access public
 	 */
 	public static function Pounds2Kg($pounds, $ounces=0)
 	{
 		return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5);
 	}
 
 	/**
 	 * Formats file/memory size in nice way
 	 *
 	 * @param int $bytes
 	 * @return string
 	 * @access public
 	 */
 	public static function formatSize($bytes)
 	{
 		if ($bytes >= 1099511627776) {
 			$return = round($bytes / 1024 / 1024 / 1024 / 1024, 2);
 			$suffix = "TB";
 		} elseif ($bytes >= 1073741824) {
 			$return = round($bytes / 1024 / 1024 / 1024, 2);
 			$suffix = "GB";
 		} elseif ($bytes >= 1048576) {
 			$return = round($bytes / 1024 / 1024, 2);
 			$suffix = "MB";
 		} elseif ($bytes >= 1024) {
 			$return = round($bytes / 1024, 2);
 			$suffix = "KB";
 		} else {
 			$return = $bytes;
 			$suffix = "Byte";
 		}
 
 		$return .= ' '.$suffix;
 
 		return $return;
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param resource $filePointer the file resource to write to
 	 * @param Array $data the data to write out
 	 * @param string $delimiter the field separator
 	 * @param string $enclosure symbol to enclose field data to
 	 * @param string $recordSeparator symbols to separate records with
 	 * @access public
 	 */
 	public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
 	{
 		foreach($data as $field_index => $field_value) {
 			// replaces an enclosure with two enclosures
 			$data[$field_index] = str_replace($enclosure, $enclosure.$enclosure, $field_value);
 		}
 
 		$line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator;
 		$line = preg_replace('/'.preg_quote($enclosure, '/').'([0-9\.]+)'.preg_quote($enclosure, '/').'/', '$1', $line);
 		fwrite($filePointer, $line);
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param resource $filePointer the file resource to write to
 	 * @param Array $data the data to write out
 	 * @param string $delimiter the field separator
 	 * @param string $enclosure symbol to enclose field data to
 	 * @param string $recordSeparator symbols to separate records with
 	 * @access public
 	 */
 	public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
 	{
 		foreach($data as $field_index => $field_value) {
 			// replaces an enclosure with two enclosures
 			$data[$field_index] = str_replace($enclosure, $enclosure.$enclosure, $field_value);
 		}
 
 		$line = $enclosure.implode($enclosure.$delimiter.$enclosure, $data).$enclosure.$recordSeparator;
 		$line = preg_replace('/'.preg_quote($enclosure, '/').'([0-9\.]+)'.preg_quote($enclosure, '/').'/', '$1', $line);
 
 		return $line;
 	}
 
 	/**
 	 * Allows to replace #section# within any string with current section
 	 *
 	 * @param string $string
 	 * @return string
 	 * @access public
 	 */
 	public static function replaceModuleSection($string)
 	{
 		$application =& kApplication::Instance();
 		$module_section = $application->RecallVar('section');
 
 		if ($module_section) {
 			// substitute section instead of #section# parameter in title preset name
 			$module_section = explode(':', $module_section);
 			$section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]);
 			$string = str_replace('#section#', mb_strtolower($section), $string);
 		}
 
 		return $string;
 	}
 
 	/**
 	 * Checks, that user IP address is within allowed range
 	 *
 	 * @param string $ip_list semi-column (by default) separated ip address list
 	 * @param string $separator ip address separator (default ";")
 	 *
 	 * @return bool
 	 * @access public
 	 */
 	public static function ipMatch($ip_list, $separator = ';')
 	{
 		if ( !isset($_SERVER['REMOTE_ADDR']) ) {
 			// PHP CLI used -> never match
 			return false;
 		}
 
 		$ip_match = false;
 		$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();
 
 		foreach ($ip_addresses as $ip_address) {
 			if (self::netMatch($ip_address, $_SERVER['REMOTE_ADDR'])) {
 				$ip_match = true;
 				break;
 			}
 		}
 
 		return $ip_match;
 	}
 
 	/**
 	 * Checks, that given ip belongs to given subnet
 	 *
 	 * @param string $network
 	 * @param string $ip
 	 * @return bool
 	 * @access public
 	 */
 	public static function netMatch($network, $ip)
 	{
 		$network = trim($network);
 		$ip = trim($ip);
 
 		if ($network == $ip) {
 			// comparing two ip addresses directly
 			return true;
 		}
 
 		$d = strpos($network, '-');
 		if ($d !== false) {
 			// ip address range specified
 			$from = ip2long(trim(substr($network, 0, $d)));
 			$to = ip2long(trim(substr($network, $d + 1)));
 
 			$ip = ip2long($ip);
 			return ($ip >= $from && $ip <= $to);
 		}
 		elseif (strpos($network, '/') !== false) {
 			// sigle subnet specified
 			$ip_arr = explode('/', $network);
 
 			if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) {
 				$ip_arr[0] .= '.0';    // Alternate form 194.1.4/24
 			}
 
 			$network_long = ip2long($ip_arr[0]);
 			$x = ip2long($ip_arr[1]);
 
 			$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
 			$ip_long = ip2long($ip);
 
 			return ($ip_long & $mask) == ($network_long & $mask);
 		}
 
 		return false;
 	}
 }
 
 /**
  * Returns array value if key exists
  * Accepts infinite number of parameters
  *
  * @param Array $array searchable array
  * @param int $key array key
  * @return string
  */
 function getArrayValue(&$array, $key)
 {
 	$ret = isset($array[$key]) ? $array[$key] : false;
 
 	if ($ret && func_num_args() > 2) {
 		for ($i = 2; $i < func_num_args(); $i++) {
 			$cur_key = func_get_arg($i);
 			$ret = getArrayValue( $ret, $cur_key );
 
 			if ($ret === false) {
 				break;
 			}
 		}
 	}
 
 	return $ret;
 }
 
 if ( !function_exists('parse_ini_string') ) {
 	/**
 	 * Equivalent for "parse_ini_string" function available since PHP 5.3.0
 	 *
 	 * @param string $ini
 	 * @param bool $process_sections
 	 * @param int $scanner_mode
 	 * @return Array
 	 */
 	function parse_ini_string($ini, $process_sections = false, $scanner_mode = null)
 	{
 		# Generate a temporary file.
 		$tempname = tempnam('/tmp', 'ini');
 		$fp = fopen($tempname, 'w');
 		fwrite($fp, $ini);
 		$ini = parse_ini_file($tempname, !empty($process_sections));
 		fclose($fp);
 		@unlink($tempname);
 
 		return $ini;
 	}
 }
 
 if ( !function_exists('memory_get_usage') ) {
 	// PHP 4.x and compiled without --enable-memory-limit option
 	function memory_get_usage() { return -1; }
 }
 
 if ( !function_exists('imagecreatefrombmp') ) {
 	// just in case if GD will add this function in future
 	function imagecreatefrombmp($filename)
 	{
 		//Ouverture du fichier en mode binaire
 		if (! $f1 = fopen($filename,"rb")) return FALSE;
 
 		//1 : Chargement des ent�tes FICHIER
 		$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14));
 		if ($FILE['file_type'] != 19778) return FALSE;
 
 		//2 : Chargement des ent�tes BMP
 		$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
 		'/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
 		'/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40));
 		$BMP['colors'] = pow(2,$BMP['bits_per_pixel']);
 		if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
 		$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
 		$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
 		$BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
 		$BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
 		$BMP['decal'] = 4-(4*$BMP['decal']);
 		if ($BMP['decal'] == 4) $BMP['decal'] = 0;
 
 		//3 : Chargement des couleurs de la palette
 		$PALETTE = array();
 		if ($BMP['colors'] < 16777216)
 		{
 			$PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
 		}
 
 		//4 : Cr�ation de l'image
 		$IMG = fread($f1,$BMP['size_bitmap']);
 		$VIDE = chr(0);
 
 		$res = imagecreatetruecolor($BMP['width'],$BMP['height']);
 		$P = 0;
 		$Y = $BMP['height']-1;
 		while ($Y >= 0)
 		{
 			$X=0;
 			while ($X < $BMP['width'])
 			{
 				if ($BMP['bits_per_pixel'] == 24)
 				$COLOR = unpack("V",substr($IMG,$P,3).$VIDE);
 				elseif ($BMP['bits_per_pixel'] == 16)
 				{
 					$COLOR = unpack("n",substr($IMG,$P,2));
 					$COLOR[1] = $PALETTE[$COLOR[1]+1];
 				}
 				elseif ($BMP['bits_per_pixel'] == 8)
 				{
 					$COLOR = unpack("n",$VIDE.substr($IMG,$P,1));
 					$COLOR[1] = $PALETTE[$COLOR[1]+1];
 				}
 				elseif ($BMP['bits_per_pixel'] == 4)
 				{
 					$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
 					if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F);
 					$COLOR[1] = $PALETTE[$COLOR[1]+1];
 				}
 				elseif ($BMP['bits_per_pixel'] == 1)
 				{
 					$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
 					if     (($P*8)%8 == 0) $COLOR[1] =  $COLOR[1]        >>7;
 					elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6;
 					elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5;
 					elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4;
 					elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3;
 					elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2;
 					elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1;
 					elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
 					$COLOR[1] = $PALETTE[$COLOR[1]+1];
 				}
 				else
 				return FALSE;
 				imagesetpixel($res,$X,$Y,$COLOR[1]);
 				$X++;
 				$P += $BMP['bytes_per_pixel'];
 			}
 			$Y--;
 			$P+=$BMP['decal'];
 		}
 
 		//Fermeture du fichier
 		fclose($f1);
 
 		return $res;
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/units/users/users_event_handler.php
===================================================================
--- branches/5.2.x/core/units/users/users_event_handler.php	(revision 14183)
+++ branches/5.2.x/core/units/users/users_event_handler.php	(revision 14184)
@@ -1,1661 +1,1663 @@
 <?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 UsersEventHandler extends kDBEventHandler
 	{
 		/**
 		 * Allows to override standart permission mapping
 		 *
 		 */
 		function mapPermissions()
 		{
 			parent::mapPermissions();
 			$permissions = Array (
 				// admin
 				'OnSetPersistantVariable'	=>	Array('self' => 'view'), // because setting to logged in user only
 				'OnUpdateRootPassword'		=>	Array('self' => true),
 				'OnUpdatePassword'		=>	Array('self' => true),
 
 				// front
 				'OnRefreshForm'				=>	Array('self' => true),
 
 				'OnForgotPassword'			=>	Array('self' => true),
 				'OnResetPassword'			=>	Array('self' => true),
 				'OnResetPasswordConfirmed'	=>	Array('self' => true),
 
 				'OnSubscribeQuery'			=>	Array('self' => true),
 				'OnSubscribeUser'			=>	Array('self' => true),
 
 				'OnRecommend'				=>	Array('self' => true),
 
 				'OnItemBuild'				=>	Array('self' => true),
 				'OnMassResetSettings'	=> Array('self' => 'edit'),
 				'OnMassCloneUsers'	=> Array('self' => 'add'),
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Shows only admins when required
 		 *
 		 * @param kEvent $event
 		 */
 		function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			if ($event->Special == 'admins') {
 				$object->addFilter('primary_filter', 'ug.GroupId = 11');
 			}
 
 			if ($event->Special == 'regular') {
 				$object->addFilter('primary_filter', 'ug.GroupId <> 11 OR ug.GroupId IS NULL');
 			}
 
 			if (!$this->Application->isAdminUser) {
 				$object->addFilter('status_filter', '%1$s.Status = '.STATUS_ACTIVE);
 			}
 
 			if ($event->Special == 'online') {
 				$object->addFilter('online_users_filter', 's.PortalUserId IS NOT NULL');
 			}
 
 			if ($event->Special == 'group') {
 				$group_id = $this->Application->GetVar('g_id');
 				if ($group_id !== false) {
 					// show only users, that user doesn't belong to current group
 					$table_name = $this->Application->GetTempName(TABLE_PREFIX.'UserGroup', 'prefix:g');
 					$sql = 'SELECT PortalUserId
 							FROM ' . $table_name . '
 							WHERE GroupId = ' . (int)$group_id;
 					$user_ids = $this->Conn->GetCol($sql);
 //					array_push($user_ids); // Guest & Everyone groups are set dynamically
 					if ($user_ids) {
 						$object->addFilter('already_member_filter', '%1$s.PortalUserId NOT IN ('.implode(',', $user_ids).')');
 					}
 				}
 			}
 		}
 
 		/**
 		 * Checks permissions of user
 		 *
 		 * @param kEvent $event
 		 */
 		function CheckPermission(&$event)
 		{
 			if ($event->Name == 'OnLogin' || $event->Name == 'OnLogout') {
 				// permission is checked in OnLogin event directly
 				return true;
 			}
 
 			if (!$this->Application->isAdminUser) {
 				$user_id = $this->Application->RecallVar('user_id');
 				$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 				if ($event->Name == 'OnCreate' && $user_id == USER_GUEST) {
 					// "Guest" can create new users
 					return true;
 				}
 
 				if ($event->Name == 'OnUpdate' && $user_id > 0) {
 					$user_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true));
 					foreach ($items_info as $id => $field_values) {
 						if ($id != $user_id) {
 							// registered users can update their record only
 							return false;
 						}
 
 						$user_dummy->Load($id);
 						$status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField'));
 
 						if ($user_dummy->GetDBField($status_field) != STATUS_ACTIVE) {
 							// not active user is not allowed to update his record (he could not activate himself manually)
 							return false;
 						}
 
 						if (isset($field_values[$status_field]) && $user_dummy->GetDBField($status_field) != $field_values[$status_field]) {
 							// user can't change status by himself
 							return false;
 						}
 					}
 					return true;
 				}
 
 				if ($event->Name == 'OnUpdate' && $user_id <= 0) {
 					// guests are not allowed to update their record, because they don't have it :)
 					return false;
 				}
 			}
 
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Handles session expiration (redirects to valid template)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSessionExpire(&$event)
 		{
 			$this->Application->resetCounters('UserSession');
 
 			// place 2 of 2 (also in kHTTPQuery::getRedirectParams)
 			$admin_url_params = Array (
 				'm_cat_id' => 0, // category means nothing on admin login screen
 				'm_wid' => '', // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets)
 				'pass' => 'm', // don't pass any other (except "m") prefixes to admin session expiration template
 				'expired' => 1, // expiration mark to show special error on login screen
 				'no_pass_through' => 1, // this way kApplication::HREF won't add them again
 			);
 
 			if ($this->Application->isAdmin) {
 				$this->Application->Redirect('index', $admin_url_params, '', 'index.php');
 			}
 
 			if ($this->Application->GetVar('admin') == 1) {
 				// Front-End showed in admin's right frame
 				$session_admin =& $this->Application->recallObject('Session.admin');
 				/* @var $session_admin Session */
 
 				if (!$session_admin->LoggedIn()) {
 					// front-end session created from admin session & both expired
 					$this->Application->DeleteVar('admin');
 					$this->Application->Redirect('index', $admin_url_params, '', 'admin/index.php');
 				}
 			}
 
 			// Front-End session expiration
 			$get = $this->Application->HttpQuery->getRedirectParams();
 			$t = $this->Application->GetVar('t');
 			$get['js_redirect'] = $this->Application->ConfigValue('UseJSRedirect');
 			$this->Application->Redirect($t ? $t : 'index', $get);
 		}
 
 		/**
 		 * [AGENT] Deletes expired sessions
 		 *
 		 * @param kEvent $event
 		 */
 		function OnDeleteExpiredSessions(&$event)
 		{
 			if (defined('IS_INSTALL') && IS_INSTALL) {
 				return ;
 			}
 
 			$this->Application->Session->DeleteExpired();
 		}
 
 		/**
 		 * Checks user data and logs it in if allowed
 		 *
 		 * @param kEvent $event
 		 */
 		function OnLogin(&$event)
 		{
 			$email_as_login = $this->Application->ConfigValue('Email_As_Login');
 			$username = $this->Application->GetVar($email_as_login && !$this->Application->isAdmin ? 'email' : 'login');
 			$password = $this->Application->GetVar('password');
 			$rember_login = $this->Application->GetVar('cb_remember_login') == 1;
 
 			$user_helper =& $this->Application->recallObject('UserHelper');
 			/* @var $user_helper UserHelper */
 
 			$user_helper->event =& $event;
 			$result = $user_helper->loginUser($username, $password, false, $rember_login);
 
 			if ($result != LoginResult::OK) {
 				$object =& $user_helper->getUserObject();
 
 				if ($result == LoginResult::NO_PERMISSION) {
 					$object->SetError('ValidateLogin', 'no_permission', 'la_no_permissions');
 				}
 				else {
 					$object->SetID(USER_GUEST);
 					$object->SetError('ValidateLogin', 'invalid_password', 'la_invalid_password');
 				}
 
 				$event->status = kEvent::erFAIL;
 			}
 		}
 
 		/**
 		 * [HOOK] Auto-Logins Front-End user when "Remember Login" cookie is found
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAutoLoginUser(&$event)
 		{
 			$remember_login_cookie = $this->Application->GetVar('remember_login');
 
 			if (!$remember_login_cookie || $this->Application->isAdmin || $this->Application->LoggedIn()) {
 				return ;
 			}
 
 			$user_helper =& $this->Application->recallObject('UserHelper');
 			/* @var $user_helper UserHelper */
 
 			$user_helper->loginUser('', '', false, false, $remember_login_cookie);
 		}
 
 		/**
 		 * Called when user logs in using old in-portal
 		 *
 		 * @param kEvent $event
 		 */
 		function OnInpLogin(&$event)
 		{
 			$sync_manager =& $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
 			$sync_manager->performAction('LoginUser', $event->getEventParam('user'), $event->getEventParam('pass') );
 
 			if ($event->redirect && is_string($event->redirect)) {
 				// some real template specified instead of true
 				$this->Application->Redirect($event->redirect, $event->getRedirectParams());
 			}
 		}
 
 		/**
 		 * Called when user logs in using old in-portal
 		 *
 		 * @param kEvent $event
 		 */
 		function OnInpLogout(&$event)
 		{
 			$sync_manager =& $this->Application->recallObject('UsersSyncronizeManager', null, Array(), Array ('InPortalSyncronize'));
 			$sync_manager->performAction('LogoutUser');
 		}
 
 		function OnLogout(&$event)
 		{
 			$user_helper =& $this->Application->recallObject('UserHelper');
 			/* @var $user_helper UserHelper */
 
 			$user_helper->event =& $event;
 			$user_helper->logoutUser();
 		}
 
 		/**
 		 * Redirects user after succesfull registration to confirmation template (on Front only)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemCreate(&$event)
 		{
 			$this->saveUserImages($event);
 
 			if ($this->Application->GetVar('skip_set_primary')) return;
 			$is_subscriber = $this->Application->GetVar('IsSubscriber');
 			if(!$is_subscriber)
 			{
 				$object =& $event->getObject();
 
 				$ug_table = TABLE_PREFIX.'UserGroup';
 				if ($object->IsTempTable()) {
 					$ug_table = $this->Application->GetTempName($ug_table, 'prefix:'.$event->Prefix);
 				}
 
 				$sql = 'UPDATE '.$ug_table.'
 						SET PrimaryGroup = 0
 						WHERE PortalUserId = '.$object->GetDBField('PortalUserId');
 				$this->Conn->Query($sql);
 
 				// set primary group to user
 				if ($this->Application->isAdminUser && $this->Application->GetVar('user_group')) {
 					// while in admin you can set any group for new users
 					$group_id = $this->Application->GetVar('user_group');
 				}
 				else {
 					$group_id = $object->GetDBField('UserGroup');
 
 					if ($group_id) {
 						// check, that group is allowed for Front-End
 						$sql = 'SELECT GroupId
 								FROM ' . TABLE_PREFIX . 'PortalGroup
 								WHERE GroupId = ' . (int)$group_id . ' AND FrontRegistration = 1';
 						$group_id = $this->Conn->GetOne($sql);
 					}
 
 					if (!$group_id) {
 						// when group not selected -> use default group
 						$group_id = $this->Application->ConfigValue('User_NewGroup');
 					}
 				}
 
 				$sql = 'REPLACE INTO '.$ug_table.'(PortalUserId,GroupId,PrimaryGroup) VALUES (%s,%s,1)';
 				$this->Conn->Query( sprintf($sql, $object->GetID(), $group_id) );
 			}
 		}
 
 		/**
 		 * Login user if possible, if not then redirect to corresponding template
 		 *
 		 * @param kEvent $event
 		 */
 		function autoLoginUser(&$event)
 		{
 			$object =& $event->getObject();
 			$this->Application->SetVar('u.current_id', $object->GetID());
 
 			if ($object->GetDBField('Status') == STATUS_ACTIVE && !$this->Application->ConfigValue('User_Password_Auto')) {
 				$user_helper =& $this->Application->recallObject('UserHelper');
 				/* @var $user_helper UserHelper */
 
 				$user_helper->loginUser($object->GetDBField('Login'), $object->GetDBField('Password_plain'));
 			}
 		}
 
 
 		/**
 		 * When creating user & user with such email exists then force to use OnUpdate insted of ?
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSubstituteSubscriber(&$event)
 		{
 			$ret = false;
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info)
 			{
 				list($id, $field_values) = each($items_info);
 				$user_email = isset($field_values['Email']) ? $field_values['Email'] : false;
 				if($user_email)
 				{
 					// check if is subscriber
 					$verify_user =& $this->Application->recallObject('u.verify', null, Array('skip_autoload' => true) );
 					$verify_user->Load($user_email, 'Email');
 					if( $verify_user->isLoaded() && $verify_user->isSubscriberOnly() )
 					{
 						$items_info = Array( $verify_user->GetDBField('PortalUserId') => $field_values );
 						$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
 						$ret = true;
 					}
 				}
 			}
 
 			if( isset($event->MasterEvent) )
 			{
 				$event->MasterEvent->setEventParam('is_subscriber_only', $ret);
 			}
 			else
 			{
 				$event->setEventParam('is_subscriber_only', $ret);
 			}
 		}
 
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @param bool $dry_run
 		 * @return bool
 		 */
 		function isSubscriberOnly(&$event, $dry_run = false)
 		{
 			$event->CallSubEvent('OnSubstituteSubscriber');
 			$is_subscriber = $event->getEventParam('is_subscriber_only');
 
 			if ($dry_run) {
 				return $is_subscriber;
 			}
 
 			if ($is_subscriber) {
 				$object =& $event->getObject( Array('skip_autoload' => true) );
 				$this->OnUpdate($event);
 
 				if ($event->status == kEvent::erSUCCESS) {
 					$this->OnAfterItemCreate($event);
 					$object->SendEmailEvents();
 
 					if (!$this->Application->isAdmin && $event->redirect) {
 						$this->autoLoginUser($event);
 					}
 				}
 			}
 
 			return $is_subscriber;
 		}
 
 		/**
 		 * Creates new user
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCreate(&$event)
 		{
 			if (!$this->Application->isAdminUser) {
 				$this->setUserStatus($event);
 			}
 
 			if (!$this->isSubscriberOnly($event)) {
 				$object =& $event->getObject( Array('skip_autoload' => true) );
 				/* @var $object kDBItem */
 				if ($this->Application->ConfigValue('User_Password_Auto')) {
 					$pass = kUtil::generatePassword(rand(5,8));
 					$object->SetField('Password', $pass);
 					$object->SetField('VerifyPassword', $pass);
 					$this->Application->SetVar('user_password',$pass);
 				}
 				parent::OnCreate($event);
 
 				$this->Application->SetVar('u.current_id', $object->getID() ); // for affil:OnRegisterAffiliate after hook
 
 				$this->setNextTemplate($event);
 
 				if (!$this->Application->isAdmin && ($event->status == kEvent::erSUCCESS) && $event->redirect) {
 					$object->SendEmailEvents();
 					$this->autoLoginUser($event);
 				}
 			}
 		}
 
 		/**
 		 * Set's new user status based on config options
 		 *
 		 * @param kEvent $event
 		 */
 		function setUserStatus(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$new_users_allowed = $this->Application->ConfigValue('User_Allow_New');
 
 			switch ($new_users_allowed) {
 				case 1: // Immediate
 					$object->SetDBField('Status', STATUS_ACTIVE);
 					$next_template = $this->Application->GetVar('registration_confirm_template');
 					if ($next_template) {
 						$event->redirect = $next_template;
 					}
 					break;
 
 				case 3: // Upon Approval
 				case 4: // Email Activation
 					$next_template = $this->Application->GetVar('registration_confirm_pending_template');
 					if ($next_template) {
 						$event->redirect = $next_template;
 					}
 					$object->SetDBField('Status', STATUS_PENDING);
 					break;
 
 				case 2: // Not Allowed
 					$object->SetDBField('Status', STATUS_DISABLED);
 					break;
 			}
 		}
 
 		/**
 		 * Set's new unique resource id to user
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemCreate(&$event)
 		{
 			parent::OnBeforeItemCreate($event);
 
 			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			if (!$this->isSubscriberOnly($event, true)) {
 				$cs_helper->CheckStateField($event, 'State', 'Country');
 			}
 
 			$this->_makePasswordRequired($event);
 			$cs_helper->PopulateStates($event, 'State', 'Country');
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( $this->Application->ConfigValue('Email_As_Login') ) {
 				$field_options = $object->GetFieldOptions('Email');
 				$field_options['error_msgs']['unique'] = $this->Application->Phrase('lu_user_and_email_already_exist');
 				$object->SetFieldOptions('Email', $field_options);
 			}
 
 			$object->setLogin();
 
 			$user_helper =& $this->Application->recallObject('UserHelper');
 			/* @var $user_helper UserHelper */
 
 			if (!$user_helper->checkBanRules($object)) {
 				$event->status = kEvent::erFAIL;
 				return ;
 			}
 		}
 
 		/**
 		 * Set's new unique resource id to user
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemValidate(&$event)
 		{
 			$object =& $event->getObject();
 			$resource_id = $object->GetDBField('ResourceId');
 			if (!$resource_id)
 			{
 				$object->SetDBField('ResourceId', $this->Application->NextResourceId() );
 			}
 		}
 
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnRecommend(&$event)
 		{
 			$friend_email = $this->Application->GetVar('friend_email');
 			$friend_name = $this->Application->GetVar('friend_email');
 
 			// used for error reporting only -> rewrite code + theme (by Alex)
 			$object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
 			/* @var $object UsersItem */
 
 			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $friend_email))
 		    {
 		    	/*$cutoff = adodb_mktime() + (int)$this->Application->ConfigValue('Suggest_MinInterval');
 				$sql = 'SELECT *
 						FROM ' . TABLE_PREFIX . 'SuggestMail
 						WHERE email = ' . $this->Conn->qstr($friend_email) . ' AND sent < ' . $cutoff;
              	if ($this->Conn->GetRow($sql) !== false) {
              		$object->SetError('Email', 'send_error', 'lu_email_already_suggested');
 					$event->status = kEvent::erFAIL;
 					return ;
              	}*/
 
 		    	$send_params = Array ();
 				$send_params['to_email'] = $friend_email;
 				$send_params['to_name'] = $friend_name;
 
 				$user_id = $this->Application->RecallVar('user_id');
 				$email_event =& $this->Application->EmailEventUser('USER.SUGGEST', $user_id, $send_params);
 				$email_event =& $this->Application->EmailEventAdmin('USER.SUGGEST');
 
 				if ($email_event->status == kEvent::erSUCCESS){
 					/*$fields_hash = Array (
 						'email' => $friend_email,
 						'sent' => adodb_mktime(),
 					);
 
 					$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'SuggestMail');*/
 
 					$event->setRedirectParams(Array('opener' => 's', 'pass' => 'all'), true);
 					$event->redirect = $this->Application->GetVar('template_success');
 				}
 				else {
 //					$event->setRedirectParams(Array('opener' => 's', 'pass' => 'all'), true);
 //					$event->redirect = $this->Application->GetVar('template_fail');
 
 					$object->SetError('Email', 'send_error', 'lu_email_send_error');
 					$event->status = kEvent::erFAIL;
 				}
 		    }
 		    else {
 		    	$object->SetError('Email', 'invalid_email', 'lu_InvalidEmail');
 				$event->status = kEvent::erFAIL;
 		    }
 		}
 
 		/**
 		 * Saves address changes and mades no redirect
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdateAddress(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 
 			if ($items_info) {
 				list ($id, $field_values) = each($items_info);
 				if ($id > 0) {
 					$object->Load($id);
 				}
 
 				$object->SetFieldsFromHash($field_values);
 				$object->setID($id);
 				$object->Validate();
 			}
 
 			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			$cs_helper->PopulateStates($event, 'State', 'Country');
 
 			$event->redirect = false;
 		}
 
 		/**
 		 * Validate subscriber's email & store it to session -> redirect to confirmation template
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSubscribeQuery(&$event)
 		{
 			$user_email = $this->Application->GetVar('subscriber_email');
 			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $user_email)) {
 
 				$object =& $this->Application->recallObject($this->Prefix.'.subscriber', null, Array('skip_autoload' => true));
 				/* @var $object UsersItem */
 
 				$this->Application->StoreVar('SubscriberEmail', $user_email);
 
 				$object->Load($user_email, 'Email');
 				if ($object->isLoaded()) {
 					$group_info = $this->GetGroupInfo($object->GetID());
 					$event->redirect = $this->Application->GetVar($group_info ? 'unsubscribe_template' : 'subscribe_template');
 				}
 				else {
 					$event->redirect = $this->Application->GetVar('subscribe_template');
 					$this->Application->StoreVar('SubscriberEmail', $user_email);
 				}
 			}
 			else {
 				// used for error reporting only -> rewrite code + theme (by Alex)
 				$object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
 				/* @var $object UsersItem */
 
 				$object->SetError('SubscribeEmail', 'invalid_email', 'lu_InvalidEmail');
 				$event->status = kEvent::erFAIL;
 			}
 		}
 
 		/**
 		 * Subscribe/Unsubscribe user based on email stored in previous step
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSubscribeUser(&$event)
 		{
 			$object = &$this->Application->recallObject($this->Prefix.'.subscriber', null, Array('skip_autoload' => true));
 			/* @var $object UsersItem */
 
 			$user_email = $this->Application->RecallVar('SubscriberEmail');
 			if (preg_match('/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', $user_email)) {
 				$this->RemoveRequiredFields($object);
 				$object->Load($user_email, 'Email');
 
 				if ($object->isLoaded()) {
 					$group_info = $this->GetGroupInfo($object->GetID());
 
 					if ($group_info){
 						if ($event->getEventParam('no_unsubscribe')) return;
 
 						if ($group_info['PrimaryGroup']){
 							// delete user
 							$object->Delete();
 						}
 						else {
 							$this->RemoveSubscriberGroup($object->GetID());
 						}
 
 						$event->redirect = $this->Application->GetVar('unsubscribe_ok_template');
 					}
 					else {
 						$this->AddSubscriberGroup($object->GetID(), 0);
 						$event->redirect = $this->Application->GetVar('subscribe_ok_template');
 					}
 				}
 				else {
 					$object->SetField('Email', $user_email);
 					$object->SetField('Login', $user_email);
 					$object->SetDBField('dob', 1);
 					$object->SetDBField('dob_date', 1);
 					$object->SetDBField('dob_time', 1);
 					$object->SetDBField('Status', STATUS_ACTIVE); // make user subscriber Active by default
 					$ip = getenv('HTTP_X_FORWARDED_FOR')?getenv('HTTP_X_FORWARDED_FOR'):getenv('REMOTE_ADDR');
 					$object->SetDBField('ip', $ip);
 
 					$this->Application->SetVar('IsSubscriber', 1);
 
 					if ($object->Create()) {
 						$this->AddSubscriberGroup($object->GetID(), 1);
 						$event->redirect = $this->Application->GetVar('subscribe_ok_template');
 					}
 
 					$this->Application->SetVar('IsSubscriber', 0);
 				}
 			}
 		}
 
 		function AddSubscriberGroup($user_id, $is_primary)
 		{
 			$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
 			$sql = 'INSERT INTO ' . TABLE_PREFIX . 'UserGroup
 						(PortalUserId, GroupId, PrimaryGroup) VALUES (%s, %s, ' . $is_primary . ')';
 			$this->Conn->Query( sprintf($sql, $user_id, $group_id) );
 
 			$this->Application->EmailEventAdmin('USER.SUBSCRIBE');
 			$this->Application->EmailEventUser('USER.SUBSCRIBE', $user_id);
 		}
 
 		function RemoveSubscriberGroup($user_id)
 		{
 			$group_id = $this->Application->ConfigValue('User_SubscriberGroup');
 			$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserGroup
 						WHERE PortalUserId = ' . $user_id . '
 						AND GroupId = ' . $this->Application->ConfigValue('User_SubscriberGroup');
 			$this->Conn->Query($sql);
 
 			$this->Application->EmailEventAdmin('USER.UNSUBSCRIBE');
 			$this->Application->EmailEventUser('USER.UNSUBSCRIBE', $user_id);
 		}
 
 		/**
 		 * Allows to detect user subscription status (subscribed or not)
 		 *
 		 * @param int $user_id
 		 * @return bool
 		 */
 		function GetGroupInfo($user_id)
 		{
 			$sql = 'SELECT * FROM ' . TABLE_PREFIX . 'UserGroup
 						WHERE (PortalUserId = ' . $user_id . ')
 						AND (GroupId = ' . $this->Application->ConfigValue('User_SubscriberGroup') . ')';
 			return $this->Conn->GetRow($sql);
 		}
 
 		function OnForgotPassword(&$event)
 		{
 			$user_object =& $this->Application->recallObject('u.forgot', null, Array('skip_autoload' => true));
 			/* @var $user_object UsersItem */
 
 			// used for error reporting only -> rewrite code + theme (by Alex)
 			$user_current_object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
 			/* @var $user_current_object UsersItem */
 
 			$username = $this->Application->GetVar('username');
 			$email = $this->Application->GetVar('email');
 			$found = false;
 			$allow_reset = true;
 
 			if (strlen($username)) {
 				$user_object->Load($username, 'Login');
 				if ($user_object->isLoaded()) {
 					$found = ($user_object->GetDBField("Login")==$username && $user_object->GetDBField("Status")==1) && strlen($user_object->GetDBField("Password"));
 				}
 			}
 			else if(strlen($email)) {
 				$user_object->Load($email, 'Email');
 				if ($user_object->isLoaded()) {
 					$found = ($user_object->GetDBField("Email")==$email && $user_object->GetDBField("Status")==1) && strlen($user_object->GetDBField("Password"));
 				}
 			}
 
 			if ($user_object->isLoaded()) {
 				$PwResetConfirm 	= $user_object->GetDBField('PwResetConfirm');
 				$PwRequestTime 		= $user_object->GetDBField('PwRequestTime');
 				$PassResetTime 		= $user_object->GetDBField('PassResetTime');
 				//$MinPwResetDelay 	= $user_object->GetDBField('MinPwResetDelay');
 				$MinPwResetDelay 	= $this->Application->ConfigValue('Users_AllowReset');
 
 				$allow_reset = (strlen($PwResetConfirm) ?
 				adodb_mktime() > $PwRequestTime + $MinPwResetDelay :
 				adodb_mktime() > $PassResetTime + $MinPwResetDelay);
 			}
 
 			if ($found && $allow_reset) {
 				$this->Application->StoreVar('tmp_user_id', $user_object->GetDBField("PortalUserId"));
 				$this->Application->StoreVar('tmp_email', $user_object->GetDBField("Email"));
 
 				$confirm_template = $this->Application->GetVar('reset_confirm_template');
 				if (!$confirm_template) {
 					$this->Application->SetVar('reset_confirm_template', 'platform/login/forgotpass_reset');
 				}
 				$this->Application->EmailEventUser('USER.PSWDC', $user_object->GetDBField('PortalUserId'));
 
 				$event->redirect = $this->Application->GetVar('template_success');
 			}
 			else {
 				if (!strlen($username) && !strlen($email)) {
 					$user_current_object->SetError('Login', 'forgotpw_nodata', 'lu_ferror_forgotpw_nodata');
 					$user_current_object->SetError('Email', 'forgotpw_nodata', 'lu_ferror_forgotpw_nodata');
 				}
 				else {
 					if ($allow_reset) {
 						if (strlen($username)) {
 							$user_current_object->SetError('Login', 'unknown_username', 'lu_ferror_unknown_username');
 						}
 						if (strlen($email)) {
 							$user_current_object->SetError('Email', 'unknown_email', 'lu_ferror_unknown_email');
 						}
 					}
 					else {
 						if (strlen($username)) {
 							$user_current_object->SetError('Login', 'reset_denied', 'lu_ferror_reset_denied');
 						}
 
 						if (strlen($email)) {
 							$user_current_object->SetError('Email', 'reset_denied', 'lu_ferror_reset_denied');
 						}
 					}
 				}
 
 				if ( $user_current_object->HasErrors() ) {
 					$event->redirect = false;
 				}
 			}
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnResetPassword(&$event)
 		{
 			$user_object =& $this->Application->recallObject('u.forgot');
 
 			if($user_object->Load($this->Application->RecallVar('tmp_user_id'))){
 
 				$this->Application->EmailEventUser('USER.PSWDC', $user_object->GetDBField("PortalUserId"));
 				$event->redirect = $this->Application->GetVar('template_success');
 
 				$m_cat_id = $this->Application->findModule('Name', 'In-Commerce', 'RootCat');
 				$this->Application->SetVar('m_cat_id', $m_cat_id);
 				$event->SetRedirectParam('pass', 'm');
 			}
 		}
 
 		function OnResetPasswordConfirmed(&$event)
 		{
 	     	// used for error reporting only -> rewrite code + theme (by Alex)
 	     	$user_current_object =& $this->Application->recallObject('u', null, Array('skip_autoload' => true));// TODO: change theme too
 			/* @var $user_current_object UsersItem */
 
 			$passed_key = trim($this->Application->GetVar('user_key'));
 
 		    if (!$passed_key) {
 				$event->setRedirectParams(Array('opener' => 's', 'pass' => 'all'), true);
 				$event->redirect = false;
 
 				$user_current_object->SetError('PwResetConfirm', 'code_is_not_valid', 'lu_code_is_not_valid');
 		    }
 
 		    $user_object =& $this->Application->recallObject('u.forgot', null, Array('skip_autoload' => true));
 			/* @var $user_object UsersItem */
 
 			$user_object->Load($passed_key, 'PwResetConfirm');
 
 		    if ($user_object->isLoaded()) {
 		    	$exp_time = $user_object->GetDBField('PwRequestTime') + 3600;
 		    	$user_object->SetDBField('PwResetConfirm', '');
 		      	$user_object->SetDBField('PwRequestTime', 0);
 
 		      	if ($exp_time > adodb_mktime()) {
 			    	$newpw = kUtil::generatePassword();
 
 			    	$this->Application->StoreVar('password', $newpw);
 
 			      	$user_object->SetField('Password', $newpw);
 			      	$user_object->SetField('VerifyPassword', $newpw);
 
 			      	$user_object->SetDBField('PassResetTime', adodb_mktime());
 			      	$user_object->SetDBField('PwResetConfirm', '');
 			      	$user_object->SetDBField('PwRequestTime', 0);
 			      	$user_object->Update();
 
 			      	$this->Application->SetVar('ForgottenPassword', $newpw);
 
 			     	$email_event_user 	=& $this->Application->EmailEventUser('USER.PSWD', $user_object->GetDBField('PortalUserId'));
 			     	$email_event_admin 	=& $this->Application->EmailEventAdmin('USER.PSWD');
 
 			     	$this->Application->DeleteVar('ForgottenPassword');
 
 			      	if ($email_event_user->status == kEvent::erSUCCESS) {
 						$event->setRedirectParams(array('opener' => 's', 'pass' => 'all'), true);
 						$event->redirect = $this->Application->GetVar('template_success');
 					}
 		      	} else {
 		      		$user_current_object->SetError('PwResetConfirm', 'code_expired', 'lu_code_expired');
 		      		$event->redirect = false;
 		      	}
 		    } else {
 		    	$user_current_object->SetError('PwResetConfirm', 'code_is_not_valid', 'lu_code_is_not_valid');
 		    	$event->redirect = false;
 		    }
 		}
 
 		function OnUpdate(&$event)
 		{
 			parent::OnUpdate($event);
 
 			$this->setNextTemplate($event);
 		}
 
 		/**
 		 * Checks state against country
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemUpdate(&$event)
 		{
 			parent::OnBeforeItemUpdate($event);
 
 			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			$cs_helper->CheckStateField($event, 'State', 'Country');
 			$cs_helper->PopulateStates($event, 'State', 'Country');
 
 			$object =& $event->getObject();
 			/* @var $object UsersItem */
 
 			$object->setLogin();
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function setNextTemplate(&$event)
 		{
 			if ($this->Application->isAdmin) {
 				return ;
 			}
 
 			$event->SetRedirectParam('opener', 's');
 			$object =& $event->getObject();
 
 			if ($object->GetDBField('Status') == STATUS_ACTIVE) {
 				$next_template = $this->Application->GetVar('next_template');
 
 				if ($next_template) {
 					$event->redirect = $next_template;
 				}
 			}
 		}
 
 		/**
 		 * Delete users from groups if their membership is expired
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCheckExpiredMembership(&$event)
 		{
 			// send pre-expiration reminders: begin
 			$pre_expiration = adodb_mktime() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24;
 			$sql = 'SELECT PortalUserId, GroupId
 					FROM '.TABLE_PREFIX.'UserGroup
 					WHERE (MembershipExpires IS NOT NULL) AND (ExpirationReminderSent = 0) AND (MembershipExpires < '.$pre_expiration.')';
 
 			$skip_clause = $event->getEventParam('skip_clause');
 			if ($skip_clause) {
 				$sql .= ' AND !('.implode(') AND !(', $skip_clause).')';
 			}
 
 			$records = $this->Conn->Query($sql);
 			if ($records) {
 				$conditions = Array();
 				foreach ($records as $record) {
 					$email_event_user =& $this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRATION.NOTICE', $record['PortalUserId']);
 					$email_event_admin =& $this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRATION.NOTICE');
 					$conditions[] = '(PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'].')';
 				}
 				$sql = 'UPDATE '.TABLE_PREFIX.'UserGroup
 						SET ExpirationReminderSent = 1
 						WHERE '.implode(' OR ', $conditions);
 				$this->Conn->Query($sql);
 			}
 			// send pre-expiration reminders: end
 
 			// remove users from groups with expired membership: begin
 			$sql = 'SELECT PortalUserId
 					FROM '.TABLE_PREFIX.'UserGroup
 					WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
 			$user_ids = $this->Conn->GetCol($sql);
 			if ($user_ids) {
 				foreach ($user_ids as $id) {
 					$email_event_user =& $this->Application->EmailEventUser('USER.MEMBERSHIP.EXPIRED', $id);
 					$email_event_admin =& $this->Application->EmailEventAdmin('USER.MEMBERSHIP.EXPIRED');
 				}
 			}
 			$sql = 'DELETE FROM '.TABLE_PREFIX.'UserGroup
 					WHERE (MembershipExpires IS NOT NULL) AND (MembershipExpires < '.adodb_mktime().')';
 			$this->Conn->Query($sql);
 			// remove users from groups with expired membership: end
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnRefreshForm(&$event)
 		{
 			$event->redirect = false;
 			$item_info = $this->Application->GetVar($event->getPrefixSpecial());
 			list($id, $fields) = each($item_info);
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$object->setID($id);
 			$object->IgnoreValidation = true;
 			$object->SetFieldsFromHash($fields);
 		}
 
 		/**
 		 * Sets persistant variable
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetPersistantVariable(&$event)
 		{
 			$field =  $this->Application->GetVar('field');
 			$value = $this->Application->GetVar('value');
 			$this->Application->StorePersistentVar($field, $value);
 
 			$force_tab = $this->Application->GetVar('SetTab');
 			if ($force_tab) {
 				$this->Application->StoreVar('force_tab', $force_tab);
 			}
 		}
 
 		/**
 		 * Overwritten to return user from order by special .ord
 		 *
 		 * @param kEvent $event
 		 */
 		function getPassedID(&$event)
 		{
 			switch ($event->Special) {
 				case 'ord':
 					$order =& $this->Application->recallObject('ord');
 					/* @var $order OrdersItem */
 
 					$id = $order->GetDBField('PortalUserId');
 					break;
 
 				case 'profile':
 					$id = $this->Application->GetVar('user_id');
 					if (!$id) {
 						// if none user_id given use current user id
 						$id = $this->Application->RecallVar('user_id');
 					}
 					break;
 
 				default:
 					$id = parent::getPassedID($event);
 					break;
 			}
 
 			return $id;
 		}
 
 		/**
 		 * Allows to change root password
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdateRootPassword(&$event)
 		{
 			return $this->OnUpdatePassword($event);
 		}
 
 		/**
 		 * Allows to change root password
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdatePassword(&$event)
 		{
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if (!$items_info) return ;
 			list ($id, $field_values) = each($items_info);
 			$user_id = $this->Application->RecallVar('user_id');
 			if ($id == $user_id && ($user_id > 0 || $user_id == USER_ROOT)) {
 				$user_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true));
 				/* @var $user_dummy kDBItem */
 
 				$user_dummy->Load($id);
 				$status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField'));
 
 				if ($user_dummy->GetDBField($status_field) != STATUS_ACTIVE) {
 					// not active user is not allowed to update his record (he could not activate himself manually)
 					return false;
 				}
 			}
 
 			if ($user_id == USER_ROOT) {
 				$object =& $event->getObject( Array('skip_autoload' => true) );
 				/* @var $object UsersItem */
 
 				// put salt to user's config
 				$field_options = $object->GetFieldOptions('RootPassword');
 				$field_options['salt'] = 'b38';
-				$object->SetFieldOptions('RootPassword', $field_options);
-				$verify_options = $object->GetFieldOptions('VerifyRootPassword');
-				$verify_options['salt'] = 'b38';
-				$object->SetFieldOptions('VerifyRootPassword', $verify_options);
 
 				// this is internal hack to allow root/root passwords for dev
 				if ($this->Application->isDebugMode() && $field_values['RootPassword'] == 'root') {
-					$this->Application->ConfigHash['Min_Password'] = 4;
+					$field_options['min_length'] = 4;
 				}
 
+				$object->SetFieldOptions('RootPassword', $field_options);
+
+				$verify_options = $object->GetFieldOptions('VerifyRootPassword');
+				$verify_options['salt'] = 'b38';
+				$object->SetFieldOptions('VerifyRootPassword', $verify_options);
+
 				$this->RemoveRequiredFields($object);
 				$object->SetDBField('RootPassword', $this->Application->ConfigValue('RootPass'));
 	 			$object->SetFieldsFromHash($field_values);
 	 			$object->setID(-1);
 	 			$status = $object->Validate();
 				if ($status) {
 					// validation on, password match too
 					$fields_hash = 	Array (
 						'VariableValue' => $object->GetDBField('RootPassword')
 					);
 					$conf_table = $this->Application->getUnitOption('conf', 'TableName');
 					$this->Conn->doUpdate($fields_hash, $conf_table, 'VariableName = "RootPass"');
 					$event->SetRedirectParam('opener', 'u');
 				}
 				else {
 					$event->status = kEvent::erFAIL;
 					$event->redirect = false;
 					return;
 				}
 			}
 			else {
 				$object =& $event->getObject();
 				$object->SetFieldsFromHash($field_values);
 
 				if (!$object->Update()) {
 					$event->status = kEvent::erFAIL;
 					$event->redirect = false;
 				}
 			}
 
 			$event->SetRedirectParam('opener', 'u');
 			$event->redirect == true;
 		}
 
 		/**
 		 * Apply custom processing to item
 		 *
 		 * @param kEvent $event
 		 */
 		function customProcessing(&$event, $type)
 		{
 			if ($event->Name == 'OnCreate' && $type == 'before') {
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				// if auto password has not been set already - store real one - to be used in email events
 				if (!$this->Application->GetVar('user_password')) {
 					$this->Application->SetVar('user_password', $object->GetDirtyField('Password'));
 					$object->SetDBField('Password_plain', $object->GetDirtyField('Password'));
 				}
 
 				// validate here, because subscribing procedure should not validate captcha code
 				if ($this->Application->ConfigValue('RegistrationCaptcha')) {
 					$captcha_helper =& $this->Application->recallObject('CaptchaHelper');
 					/* @var $captcha_helper kCaptchaHelper */
 
 					$captcha_helper->validateCode($event, false);
 				}
 			}
 		}
 
 		function OnMassResetSettings(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			$default_user_id = $this->Application->ConfigValue('DefaultSettingsUserId');
 			if (in_array($default_user_id, $ids)) {
 				array_splice($ids, array_search($default_user_id, $ids), 1);
 			}
 			if ($ids) {
 				$q = 'DELETE FROM '.TABLE_PREFIX.'PersistantSessionData WHERE PortalUserId IN ('.join(',', $ids).') AND
 							 (VariableName LIKE "%_columns_%"
 							 OR
 							 VariableName LIKE "%_filter%"
 							 OR
 							 VariableName LIKE "%_PerPage%")';
 				$this->Conn->Query($q);
 			}
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Checks, that currently loaded item is allowed for viewing (non permission-based)
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function checkItemStatus(&$event)
 		{
 			$object =& $event->getObject();
 			if (!$object->isLoaded()) {
 				return true;
 			}
 
 			$virtual_users = Array (USER_ROOT, USER_GUEST);
 			return ($object->GetDBField('Status') == STATUS_ACTIVE) || in_array($object->GetID(), $virtual_users);
 		}
 
 		/**
 		 * Sends approved/declined email event on user status change
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
 			$this->saveUserImages($event);
 
 			$object =& $event->getObject();
 			/* @var $object UsersItem */
 
 			if (!$this->Application->isAdmin || $object->IsTempTable()) {
 				return ;
 			}
 
 			$this->sendStatusChangeEvent($object->GetID(), $object->GetOriginalField('Status'), $object->GetDBField('Status'));
 		}
 
 		/**
 		 * Stores user's original Status before overwriting with data from temp table
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeDeleteFromLive(&$event)
 		{
 			$user_status = $this->Application->GetVar('user_status');
 			if (!$user_status) {
 				$user_status = Array ();
 			}
 
 			$user_id = $event->getEventParam('id');
 			if ($user_id > 0) {
 				$user_status[$user_id] = $this->getUserStatus($user_id);
 				$this->Application->SetVar('user_status', $user_status);
 			}
 		}
 
 		/**
 		 * Sends approved/declined email event on user status change (in temp tables during editing)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterCopyToLive(&$event)
 		{
 			$temp_id = $event->getEventParam('temp_id');
 			if ($temp_id == 0) {
 				// this is new user create, don't send email events
 				return ;
 			}
 
 			$new_status = $this->getUserStatus($temp_id);
 			$user_status = $this->Application->GetVar('user_status');
 
 			$this->sendStatusChangeEvent($temp_id, $user_status[$temp_id], $new_status);
 		}
 
 		/**
 		 * Returns user status (active, pending, disabled) based on ID and temp mode setting
 		 *
 		 * @param int $user_id
 		 * @return int
 		 */
 		function getUserStatus($user_id)
 		{
 			$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
 
 			$sql = 'SELECT Status
 					FROM '.$table_name.'
 					WHERE '.$id_field.' = '.$user_id;
 			return $this->Conn->GetOne($sql);
 		}
 
 		/**
 		 * Sends approved/declined email event on user status change
 		 *
 		 * @param int $user_id
 		 * @param int $prev_status
 		 * @param int $new_status
 		 */
 		function sendStatusChangeEvent($user_id, $prev_status, $new_status)
 		{
 			$status_events = Array (
 				STATUS_ACTIVE	=>	'USER.APPROVE',
 				STATUS_DISABLED	=>	'USER.DENY',
 			);
 			$email_event = isset($status_events[$new_status]) ? $status_events[$new_status] : false;
 
 			if (($prev_status != $new_status) && $email_event) {
 				$this->Application->EmailEventUser($email_event, $user_id);
 				$this->Application->EmailEventAdmin($email_event);
 			}
 
 			// deletes sessions from users, that are no longer active
 			if (($prev_status != $new_status) && ($new_status != STATUS_ACTIVE)) {
 				$sql = 'SELECT SessionKey
 						FROM ' . TABLE_PREFIX . 'UserSession
 						WHERE PortalUserId = ' . $user_id;
 				$session_ids = $this->Conn->GetCol($sql);
 
 				$this->Application->Session->DeleteSessions($session_ids);
 			}
 		}
 
 		/**
 		 * OnAfterConfigRead for users
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterConfigRead(&$event)
 		{
 			parent::OnAfterConfigRead($event);
 
 			// 1. arrange user registration countries
 			$site_helper =& $this->Application->recallObject('SiteHelper');
 			/* @var $site_helper SiteHelper */
 
 			$first_country = $site_helper->getDefaultCountry('', false);
 
 			if ($first_country === false) {
 				$first_country = $this->Application->ConfigValue('User_Default_Registration_Country');
 			}
 
 			if ($first_country) {
 				// update user country dropdown sql
 				$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 				$fields['Country']['options_sql'] = preg_replace('/ORDER BY (.*)/', 'ORDER BY IF (CountryStateId = '.$first_country.', 1, 0) DESC, \\1', $fields['Country']['options_sql']);
 				$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
 			}
 
 			// 2. set default user registration group
 			$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
 			$virtual_fields['UserGroup']['default'] = $this->Application->ConfigValue('User_NewGroup');
 			$this->Application->setUnitOption($event->Prefix, 'VirtualFields', $virtual_fields);
 
 			// 3. allow avatar upload on Front-End
 			$file_helper =& $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$file_helper->createItemFiles($event->Prefix, true); // create image fields
 
 			if ($this->Application->isAdminUser) {
 				// 4. when in administrative console, then create all users with Active status
 				$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 //				$fields['Password']['required'] = 1; // set password required (will broke approve/decline buttons)
 				$fields['Status']['default'] = STATUS_ACTIVE;
 				$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
 
 				// 5. remove groups tab on editing forms when AdvancedUserManagement config variable not set
 				if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
 					$edit_tab_presets = $this->Application->getUnitOption($event->Prefix, 'EditTabPresets');
 
 					foreach ($edit_tab_presets as $preset_name => $preset_tabs) {
 						if (array_key_exists('groups', $preset_tabs)) {
 							unset($edit_tab_presets[$preset_name]['groups']);
 
 							if (count($edit_tab_presets[$preset_name]) == 1) {
 								// only 1 tab left -> remove it too
 								$edit_tab_presets[$preset_name] = Array ();
 							}
 						}
 					}
 
 					$this->Application->setUnitOption($event->Prefix, 'EditTabPresets', $edit_tab_presets);
 				}
 			}
 		}
 
 		/**
 		 * OnMassCloneUsers
 		 *
 		 * @param kEvent $event
 		 */
 		function OnMassCloneUsers(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$event->status=kEvent::erSUCCESS;
 			$ids = $this->StoreSelectedIDs($event);
 
 			$this->Application->SetVar('skip_set_primary', 1); // otherwise it will default primary group, search for skip_set_primary above
 			$temp_handler =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
 			/* @var $temp_handler kTempTablesHandler */
 			$cloned_users = $temp_handler->CloneItems($event->Prefix, '', $ids);
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * When cloning users, reset password (set random)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeClone(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 			$object->setRequired('Password', 0);
 			$object->setRequired('VerifyPassword', 0);
 			$object->SetDBField('Password', rand(100000000, 999999999));
 			$object->SetDBField('CreatedOn', adodb_mktime());
 			$object->SetDBField('ResourceId', false); // this will reset it
 
 			// change email cause it should be unique
 			$object->NameCopy(array(), $object->GetID(), 'Email', 'copy%1$s.%2$s');
 
 			$object->UpdateFormattersSubFields();
 		}
 
 		/**
 		 * Copy user groups after copying user
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterClone(&$event)
 		{
 			$id = $event->getEventParam('id');
 			$original_id = $event->getEventParam('original_id');
 
 			$sql = 'INSERT '.TABLE_PREFIX."UserGroup SELECT $id, GroupId, MembershipExpires, PrimaryGroup, 0 FROM ".TABLE_PREFIX."UserGroup WHERE PortalUserId = $original_id";
 			$this->Conn->Query($sql);
 		}
 
 		/**
 		 * Saves selected ids to session
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSaveSelected(&$event)
 		{
 			$this->StoreSelectedIDs($event);
 
 			// remove current ID, otherwise group selector will use it in filters
 			$this->Application->DeleteVar($event->getPrefixSpecial(true).'_id');
 		}
 
 		/**
 		 * Adds selected link to listing
 		 *
 		 * @param kEvent $event
 		 */
 		function OnProcessSelected(&$event)
 		{
 			$event->SetRedirectParam('opener', 'u');
 			$user_ids = $this->getSelectedIDs($event, true);
 			$this->clearSelectedIDs($event);
 
 			$dst_field = $this->Application->RecallVar('dst_field');
 			if ($dst_field != 'PrimaryGroupId') {
 				return ;
 			}
 
 			$group_ids = $this->Application->GetVar('g');
 			$primary_group_id = $group_ids ? array_shift( array_keys($group_ids) ) : false;
 
 			if (!$user_ids || !$primary_group_id) {
 				return ;
 			}
 
 			$table_name = $this->Application->getUnitOption('ug', 'TableName');
 
 			$sql = 'SELECT PortalUserId
 					FROM '.$table_name.'
 					WHERE (GroupId = '.$primary_group_id.') AND (PortalUserId IN ('.implode(',', $user_ids).'))';
 			$existing_members = $this->Conn->GetCol($sql);
 
 			// 1. reset primary group mark
 			$sql = 'UPDATE '.$table_name.'
 					SET PrimaryGroup = 0
 					WHERE PortalUserId IN ('.implode(',', $user_ids).')';
 			$this->Conn->Query($sql);
 
 			foreach ($user_ids as $user_id) {
 				if (in_array($user_id, $existing_members)) {
 					// 2. already member of that group -> just make primary
 					$sql = 'UPDATE '.$table_name.'
 							SET PrimaryGroup = 1
 							WHERE (PortalUserId = '.$user_id.') AND (GroupId = '.$primary_group_id.')';
 					$this->Conn->Query($sql);
 				}
 				else {
 					// 3. not member of that group -> make member & make primary
 					$fields_hash = Array (
 						'GroupId'		=>	$primary_group_id,
 						'PortalUserId'	=>	$user_id,
 						'PrimaryGroup'	=>	1,
 					);
 					$this->Conn->doInsert($fields_hash, $table_name);
 				}
 			}
 		}
 
 		/**
 		 * Loads user images
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemLoad(&$event)
 		{
 			parent::OnAfterItemLoad($event);
 
 			// linking existing images for item with virtual fields
 			$image_helper =& $this->Application->recallObject('ImageHelper');
 			/* @var $image_helper ImageHelper */
 
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$image_helper->LoadItemImages($object);
 
 			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			$cs_helper->PopulateStates($event, 'State', 'Country');
 		}
 
 		/**
 		 * Save user images
 		 *
 		 * @param kEvent $event
 		 */
 		function saveUserImages(&$event)
 		{
 			if (!$this->Application->isAdmin) {
 				$image_helper =& $this->Application->recallObject('ImageHelper');
 				/* @var $image_helper ImageHelper */
 
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				// process image upload in virtual fields
 				$image_helper->SaveItemImages($object);
 			}
 		}
 
 		/**
 		 * Makes password required for new users
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreCreate(&$event)
 		{
 			parent::OnPreCreate($event);
 
 			if ($event->status == kEvent::erSUCCESS) {
 				$this->_makePasswordRequired($event);
 			}
 		}
 
 		/**
 		 * Makes password required for new users
 		 *
 		 * @param kEvent $event
 		 */
 		function OnNew(&$event)
 		{
 			parent::OnNew($event);
 
 			if ($event->status == kEvent::erSUCCESS) {
 				$this->_makePasswordRequired($event);
 			}
 		}
 
 		/**
 		 * Makes password required for new users
 		 *
 		 * @param kEvent $event
 		 */
 		function _makePasswordRequired(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			$required_fields = Array ('Password', 'Password_plain', 'VerifyPassword', 'VerifyPassword_plain');
 			foreach ($required_fields as $required_field) {
 				$object->setRequired($required_field);
 			}
 		}
 
 		/**
 		 * Load item if id is available
 		 *
 		 * @param kEvent $event
 		 */
 		function LoadItem(&$event)
 		{
 			$id = $this->getPassedID($event);
 
 			if ($id < 0) {
 				// when root, guest and so on
 
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				$object->Clear($id);
 				return ;
 			}
 
 			parent::LoadItem($event);
 		}
 	}
Index: branches/5.2.x/core/units/helpers/category_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/category_helper.php	(revision 14183)
+++ branches/5.2.x/core/units/helpers/category_helper.php	(revision 14184)
@@ -1,599 +1,599 @@
 <?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 CategoryHelper extends kHelper {
 
 		/**
 		 * Structure tree for ParentId field in category or category items
 		 *
 		 * @var Array
 		 */
 		var $_structureTree = null;
 
 		/**
 		 * ID of primary language (only for caching)
 		 *
 		 * @var int
 		 */
 		var $_primaryLanguageId = false;
 
 		/**
 		 * Prints category path using given blocks. Also supports used defined path elements at the end.
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function NavigationBar($params)
 		{
 			$params['is_first'] = 1;
 			$main_category_id = isset($params['cat_id']) ? $params['cat_id'] : $this->Application->GetVar('m_cat_id');
 
 			if (array_key_exists('shift', $params) && $params['shift']) {
 				$home_element = '';
 				$params['shift']--;
 			}
 			else {
 				$home_element = $this->getHomeCategoryPath($params, $main_category_id);
 				unset($params['is_first']);
 			}
 
 			if (!getArrayValue($params, 'titles') && !getArrayValue($params, 'templates')) {
 				// no static templates given, show only category path
 				return $home_element . $this->getCategoryPath($main_category_id, $params);
 			}
 
 			$navigation_parts = $this->getNavigationParts($params['titles'], $params['templates']);
 
 			$ret = '';
 			$block_params = Array (); //$params; // sort of TagProcessor:prepareTagParams
 			$block_params['no_editing'] = 1;
 			$block_params['category'] = 0;
 			$block_params['separator'] = $params['separator'];
 			$show_category = getArrayValue($params, 'show_category');
 
 			$current_template = $this->Application->GetVar('t');
-			$physical_template = array_search($current_template, $this->Application->structureTemplateMapping);
+			$physical_template = $this->Application->getPhysicalTemplate($current_template);
 
 			if ($physical_template !== false) {
 				// replace menu template name with it's actual template name on disk
 				list ($current_template) = explode(':', $physical_template, 2);
 			}
 
 			foreach ($navigation_parts as $template => $title) {
 				$block_params['template'] = $template;
 
 				if ($title == '__item__') {
 					if ($show_category) {
 						$ret .= $this->getCategoryPath($main_category_id, $params);
 						$show_category = false;
 					}
 
 					$category_path = $this->getCategoryParentPath($main_category_id);
 					$module_info = $this->getCategoryModule($params, array_keys($category_path));
 					if (!$module_info) {
 						continue;
 					}
 
 					$module_prefix = $module_info['Var'];
 					$object =& $this->Application->recallObject($module_prefix);
 					/* @var $object kCatDBItem */
 
 					$title_field = $this->Application->getUnitOption($module_prefix, 'TitleField');
 					$block_params['title'] = $object->GetField($title_field);
 					$block_params['prefix'] = $module_prefix;
 					$block_params['current'] = 0;
 
 					$block_params['name'] = $this->SelectParam($params, 'module_item_render_as,render_as');
 				}
 				else {
 					$block_params['current'] = ($template == $current_template);
 					$block_params['title'] = $this->Application->Phrase($title);
 
 					$block_params['name'] = $template == $current_template ? $params['current_render_as'] : $params['render_as'];
 				}
 
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 
 			if ($show_category) {
 				$params['no_current'] = true;
 				return $home_element . ($show_category ? $this->getCategoryPath($main_category_id, $params) : '') . $ret;
 			}
 
 			return $home_element . $ret;
 		}
 
 		/**
 		 * Get navigation parts
 		 *
 		 * @param Array $titles
 		 * @param Array $templates
 		 * @return Array
 		 */
 		function getNavigationParts($titles, $templates)
 		{
 			$titles = explode(',', $titles);
 			$templates = explode(',', $templates);
 
 			$ret = Array ();
 			foreach ($templates as $template_pos => $template) {
 				$ret[$template] = $titles[$template_pos];
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Renders path to given category using given blocks.
 		 *
 		 * @param int $main_category_id
 		 * @param Array $params
 		 * @return string
 		 */
 		function getCategoryPath($main_category_id, $params)
 		{
 			$category_path = $this->getCategoryParentPath($main_category_id);
 			if (!$category_path) {
 				// in "Home" category
 				return '';
 			}
 
 			if (array_key_exists('shift', $params) && $params['shift']) {
 				array_splice($category_path, 0, $params['shift']);
 			}
 
 			$module_info = $this->getCategoryModule($params, array_keys($category_path));
 
 			$module_category_id = $module_info['RootCat'];
 			$module_item_id = $this->Application->GetVar($module_info['Var'].'_id');
 
 			$ret = '';
 			$block_params['category'] = 1;
 			$block_params['no_editing'] = 1;
 
 			if (array_key_exists('is_first', $params)) {
 				$block_params['is_first'] = $params['is_first'];
 			}
 
 			$block_params['separator'] = $params['separator'];
 			$no_current = isset($params['no_current']) && $params['no_current'];
 
 			$backup_category_id = $this->Application->GetVar('c_id');
 			foreach ($category_path as $category_id => $category_name) {
 				$block_params['cat_id'] = $category_id;
 				$block_params['cat_name'] = $block_params['title'] = $category_name;
 
 				if ($no_current) {
 					$block_params['current'] = 0;
 				}
 				else {
 					$block_params['current'] = ($main_category_id == $category_id) && !$module_item_id ? 1 : 0;
 				}
 
 				$block_params['is_module_root'] = $category_id == $module_category_id ? 1 : 0;
 				$block_params['name'] = $this->SelectParam($params, 'render_as,block');
 
 				// which block to parse as current ?
 				if ($block_params['is_module_root']) {
 					$block_params['name'] = $this->SelectParam($params, 'module_root_render_as,render_as');
 					$block_params['module_index'] = $module_info['TemplatePath'].'index';
 				}
 
 				if ($block_params['current']) {
 					$block_params['name'] = $this->SelectParam($params, 'current_render_as,render_as');
 				}
 
 				$this->Application->SetVar('c_id', $category_id);
 				$ret .= $this->Application->ParseBlock($block_params);
 
 				if (array_key_exists('is_first', $block_params)) {
 					unset($block_params['is_first']);
 				}
 			}
 
 			$this->Application->SetVar('c_id', $backup_category_id);
 
 			return $ret;
 		}
 
 		/**
 		 * Returns module information based on given module name or current category (relative to module root categories)
 		 *
 		 * @param Array $params
 		 * @param Array $category_ids category parent path (already as array)
 		 * @return Array
 		 */
 		function getCategoryModule($params, $category_ids)
 		{
 			if (isset($params['module'])) {
 				// get module by name specified
 				$module_info = $this->Application->findModule('Name', $params['module']);
 
 			}
 			elseif ($category_ids) {
 				// get module by category path
 				$module_root_categories = $this->getModuleRootCategories();
 				$common_categories = array_intersect($category_ids, $module_root_categories);
 				$module_category_id = array_shift($common_categories); // get 1st common category
 				$module_info = $this->Application->findModule('RootCat', $module_category_id);
 			}
 
 			return $module_info;
 		}
 
 		/**
 		 * Renders path to top catalog category
 		 *
 		 * @param Array $params
 		 * @param int $current_category
 		 * @return string
 		 */
 		function getHomeCategoryPath($params, $current_category)
 		{
 			$block_params['cat_id'] = $this->Application->getBaseCategory();
 			$block_params['no_editing'] = 1;
 			$block_params['current'] = $current_category == $block_params['cat_id'] ? 1 : 0;
 			$block_params['separator'] = $params['separator'];
 			$block_params['is_first'] = $params['is_first'];
 			$block_params['cat_name'] = $this->Application->Phrase(($this->Application->isAdmin ? 'la_' : 'lu_') . 'rootcategory_name');
 			$block_params['name'] = $this->SelectParam($params, 'root_cat_render_as,render_as');
 
 			return $this->Application->ParseBlock($block_params);
 		}
 
 		/**
 		 * Returns root categories from all modules
 		 *
 		 * @return Array
 		 */
 		function getModuleRootCategories()
 		{
 			static $root_categories = null;
 
 			if (!isset($root_categories)) {
 				$root_categories = Array ();
 				foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 					array_push($root_categories, $module_info['RootCat']);
 				}
 
 				$root_categories = array_unique($root_categories);
 			}
 
 			return $root_categories;
 		}
 
 		/**
 		 * Returns given category's parent path as array of id=>name elements
 		 *
 		 * @param int $main_category_id
 		 * @return Array
 		 */
 		function getCategoryParentPath($main_category_id)
 		{
 			if ($main_category_id == 0) {
 				// don't query path for "Home" category
 				return Array ();
 			}
 
 			$cache_key = 'parent_paths_named[%CIDSerial:' . $main_category_id . '%]';
 			$cached_path = $this->Application->getCache($cache_key);
 
 			if ($cached_path === false) {
 				$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 				$navbar_field = $ml_formatter->LangFieldName('CachedNavBar');
 
 				$id_field = $this->Application->getUnitOption('c', 'IDField');
 				$table_name = $this->Application->getUnitOption('c', 'TableName');
 
 				$this->Conn->nextQueryCachable = true;
 				$sql = 'SELECT '.$navbar_field.', ParentPath
 						FROM '.$table_name.'
 						WHERE '.$id_field.' = '.$main_category_id;
 				$category_data = $this->Conn->GetRow($sql);
 
 				$cached_path = Array ();
 				$skip_category = $this->Application->getBaseCategory();
 
 				if ($category_data) {
 					$category_names = explode('&|&', $category_data[$navbar_field]);
 					$category_ids = explode('|', substr($category_data['ParentPath'], 1, -1));
 
 					foreach ($category_ids as $category_index => $category_id) {
 						if ($category_id == $skip_category) {
 							continue;
 						}
 
 						$cached_path[$category_id] = $category_names[$category_index];
 					}
 				}
 
 				$this->Application->setCache($cache_key, $cached_path);
 			}
 
 			return $cached_path;
 		}
 
 		 /**
 		 * Not tag, method for parameter
 		 * selection from list in this TagProcessor
 		 *
 		 * @param Array $params
 		 * @param string $possible_names
 		 * @return string
 		 * @access public
 		 */
 		function SelectParam($params, $possible_names)
 		{
 			if (!is_array($params)) return;
 			if (!is_array($possible_names))
 
 			$possible_names = explode(',', $possible_names);
 			foreach ($possible_names as $name)
 			{
 				if( isset($params[$name]) ) return $params[$name];
 			}
 			return false;
 		}
 
 		/**
 		 * Converts multi-dimensional category structure in one-dimensional option array (category_id=>category_name)
 		 *
 		 * @param Array $data
 		 * @param int $parent_category_id
 		 * @param int_type $language_id
 		 * @param int $theme_id
 		 * @param int $level
 		 * @return Array
 		 */
 		function _printChildren(&$data, $parent_category_id, $language_id, $theme_id, $level = 0)
 		{
 			if ($data['ThemeId'] != $theme_id && $data['ThemeId'] != 0) {
 				// don't show system templates from different themes
 				return Array ();
 			}
 
 			$category_language = $data['l' . $language_id . '_Name'] ? $language_id : $this->_primaryLanguageId;
 			$ret = Array($parent_category_id => str_repeat('-', $level).' '.$data['l' . $category_language . '_Name']);
 
 			if ($data['children']) {
 				$level++;
 				foreach ($data['children'] as $category_id => $category_data) {
 					// numeric keys
 					$ret = kUtil::array_merge_recursive($ret, $this->_printChildren($data['children'][$category_id], $category_id, $language_id, $theme_id, $level));
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Returns information about children under parent path (recursive)
 		 *
 		 * @param int $parent_category_id
 		 * @param Array $languages
 		 * @return Array
 		 */
 		function _getChildren($parent_category_id, $languages)
 		{
 			static $items_by_parent = null, $parent_mapping = null;
 
 			if ( !isset($items_by_parent) ) {
 				$fields = $items_by_parent = Array ();
 
 				foreach ($languages as $language_id) {
 					$fields[] = 'l' . $language_id . '_Name';
 				}
 
 				$sql = 'SELECT CategoryId AS id, ' . implode(', ', $fields) . ', ParentId, ThemeId
 						FROM ' . $this->Application->getUnitOption('c', 'TableName') . '
 						ORDER BY Priority DESC';
 				$items = $this->Conn->Query($sql, 'id');
 
 				foreach ($items as $item_id => $item_data) {
 					$item_parent_id = $item_data['ParentId'];
 					unset($item_data['ParentId']);
 
 					if ( !array_key_exists($item_parent_id, $items_by_parent) ) {
 						$items_by_parent[$item_parent_id] = Array ();
 					}
 
 					$item_data['children'] = false;
 					$parent_mapping[$item_id] = $item_parent_id;
 					$items_by_parent[$item_parent_id][$item_id] = $item_data;
 				}
 			}
 
 			$data = $items_by_parent[ $parent_mapping[$parent_category_id] ][$parent_category_id];
 			$categories = array_key_exists($parent_category_id, $items_by_parent) ? $items_by_parent[$parent_category_id] : Array ();
 
 			foreach ($categories as $category_id => $category_data) {
 				if ($category_id == $parent_category_id) {
 					// don't process myself - prevents recursion
 					continue;
 				}
 
 				$data['children'][$category_id] = $this->_getChildren($category_id, $languages);
 			}
 
 			return $data;
 		}
 
 		/**
 		 * Generates OR retrieves from cache structure tree
 		 *
 		 * @return Array
 		 */
 		function &_getStructureTree()
 		{
 			// get cached version of structure tree
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$data = $this->Application->getCache('master:StructureTree', false);
 			}
 			else {
 				$data = $this->Application->getDBCache('StructureTree');
 			}
 
 			if ($data) {
 				$data = unserialize($data);
 
 				return $data;
 			}
 
 			// generate structure tree from scratch
 			$ml_helper =& $this->Application->recallObject('kMultiLanguageHelper');
 			/* @var $ml_helper kMultiLanguageHelper */
 
 			$languages = $ml_helper->getLanguages();
 			$root_category = $this->Application->getBaseCategory();
 			$data = $this->_getChildren($root_category, $languages);
 
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$this->Application->setCache('master:StructureTree', serialize($data));
 			}
 			else {
 				$this->Application->setDBCache('StructureTree', serialize($data));
 			}
 
 			return $data;
 		}
 
 		function getTemplateMapping()
 		{
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$data = $this->Application->getCache('master:template_mapping', false);
 			}
 			else {
 				$data = $this->Application->getDBCache('template_mapping');
 			}
 
 			if ($data) {
 				return unserialize($data);
 			}
 
 			$sql = 'SELECT
 						IF(c.`Type` = ' . PAGE_TYPE_TEMPLATE . ', CONCAT(c.Template, ":", c.ThemeId), CONCAT("id:", c.CategoryId)) AS SrcTemplate,
 						LOWER(
 							IF(
 								c.SymLinkCategoryId IS NOT NULL,
 								(SELECT cc.NamedParentPath FROM ' . TABLE_PREFIX . 'Category AS cc WHERE cc.CategoryId = c.SymLinkCategoryId),
 							 	c.NamedParentPath
 							 )
 						) AS DstTemplate,
 						c.UseExternalUrl, c.ExternalUrl
 					FROM ' . TABLE_PREFIX . 'Category AS c
 					WHERE c.Status = ' . STATUS_ACTIVE;
 			$pages = $this->Conn->Query($sql, 'SrcTemplate');
 
 			$mapping = Array ();
 			$base_url = $this->Application->BaseURL();
 
 			foreach ($pages as $src_template => $page) {
 				// process external url, before placing in cache
 				if ($page['UseExternalUrl']) {
 					$external_url = $page['ExternalUrl'];
 
 					if (!preg_match('/^(.*?):\/\/(.*)$/', $external_url)) {
 						// url without protocol will be relative url to our site
 						$external_url = $base_url . $external_url;
 					}
 
 					$dst_template = 'external:' . $external_url;
 				}
 				else {
 					$dst_template = preg_replace('/^Content\//i', '', $page['DstTemplate']);
 				}
 
 				$mapping[$src_template] = $dst_template;
 			}
 
 			if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 				$data = $this->Application->setCache('master:template_mapping', serialize($mapping));
 			}
 			else {
 				$this->Application->setDBCache('template_mapping', serialize($mapping));
 			}
 
 			return $mapping;
 		}
 
 		/**
 		 * Returns category structure as field option list
 		 *
 		 * @return Array
 		 */
 		function getStructureTreeAsOptions()
 		{
 			if ((defined('IS_INSTALL') && IS_INSTALL) || !$this->Application->isAdmin) {
 				// no need to create category structure during install
 				// OR on Front-End, because it's not used there
 				return Array ();
 			}
 
 			if (isset($this->_structureTree)) {
 				return $this->_structureTree;
 			}
 
 			$themes_helper =& $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			$data = $this->_getStructureTree();
 
 			$theme_id = (int)$themes_helper->getCurrentThemeId();
 			$root_category = $this->Application->getBaseCategory();
 
 			$this->_primaryLanguageId = $this->Application->GetDefaultLanguageId();
 			$this->_structureTree = $this->_printChildren($data, $root_category, $this->Application->GetVar('m_lang'), $theme_id);
 
 			return $this->_structureTree;
 		}
 
 		/**
 		 * Replace links like "@@ID@@" to actual template names in given text
 		 *
 		 * @param string $text
 		 * @return string
 		 */
 		function replacePageIds($text)
 		{
 			if (!preg_match_all('/@@(\\d+)@@/', $text, $regs)) {
 				return $text;
 			}
 
 			$page_ids = $regs[1];
 
 			$sql = 'SELECT NamedParentPath, CategoryId
 					FROM ' . TABLE_PREFIX . 'Category
 					WHERE CategoryId IN (' . implode(',', $page_ids) . ')';
 			$templates = $this->Conn->GetCol($sql, 'CategoryId');
 
 			foreach ($page_ids as $page_id) {
 				if (!array_key_exists($page_id, $templates)) {
 					// internal page was deleted, but link to it was found in given content block data
 					continue;
 				}
 
 				$url_params = Array ('m_cat_id' => $page_id, 'pass' => 'm');
 				$page_url = $this->Application->HREF(strtolower($templates[$page_id]), '', $url_params);
 				/*if ($this->Application->isAdmin) {
 					$page_url = preg_replace('/&(admin|editing_mode)=[\d]/', '', $page_url);
 				}*/
 
 				$text = str_replace('@@' . $page_id . '@@', $page_url, $text);
 			}
 
 			return $text;
 		}
 	}
\ No newline at end of file
Index: branches/5.2.x/core/units/helpers/mod_rewrite_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/mod_rewrite_helper.php	(revision 14183)
+++ branches/5.2.x/core/units/helpers/mod_rewrite_helper.php	(revision 14184)
@@ -1,1145 +1,1189 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class kModRewriteHelper extends kHelper {
 
 		/**
 		 * Holds a refererence to httpquery
 		 *
 		 * @var kHttpQuery
 		 */
 		var $HTTPQuery = null;
 
 		/**
 		 * Parts found during url parsing
 		 *
 		 * @var Array
 		 */
 		var $_partsFound = Array ();
 
 		/**
 		 * Category item prefix, that was found
 		 *
 		 * @var string
 		 */
 		var $_modulePrefix = false;
 
 		/**
 		 * Template aliases for current theme
 		 *
 		 * @var Array
 		 */
 		var $_templateAliases = null;
 
  		/**
 		 * Domain-based primary language id
 		 *
 		 * @var int
 		 */
 		var $primaryLanguageId = false;
 
 		/**
 		 * Domain-based primary theme id
 		 *
 		 * @var int
 		 */
 		var $primaryThemeId = false;
 
 		/**
 		 * Possible url endings from ModRewriteUrlEnding configuration variable
 		 *
 		 * @var Array
 		 */
 		var $_urlEndings = Array ('.html', '/', '');
 
 		/**
 		 * Constructor of kModRewriteHelper class
 		 *
 		 * @return kModRewriteHelper
 		 */
 		public function __construct()
 		{
 			parent::__construct();
 
 			$this->HTTPQuery =& $this->Application->recallObject('HTTPQuery');
 
 			// domain based primary language
 			$this->primaryLanguageId = $this->Application->siteDomainField('PrimaryLanguageId');
 
 			// domain based primary theme
 			$this->primaryThemeId = $this->Application->siteDomainField('PrimaryThemeId');
 		}
 
 		function processRewriteURL()
 		{
 			$passed = Array ();
 			$url = $this->HTTPQuery->Get('_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;
 					}
 				}
 			}
 
 			$restored = false;
 
 			$cached = $this->_getCachedUrl($url);
 
 			if ($cached !== false) {
 				$vars = $cached['vars'];
 				$passed = $cached['passed'];
 				$restored = true;
 			}
 			else {
 				$vars = $this->parseRewriteURL($url);
 				$passed = $vars['pass']; // also used in bottom of this method
 				unset($vars['pass']);
 
 				$this->_setCachedUrl($url, Array ('vars' => $vars, 'passed' => $passed));
 
 				if (array_key_exists('t', $this->HTTPQuery->Post) && $this->HTTPQuery->Post['t']) {
 					// template from POST overrides template from URL.
 					$vars['t'] = $this->HTTPQuery->Post['t'];
 					if (isset($vars['is_virtual']) && $vars['is_virtual']) {
 						$vars['m_cat_id'] = 0; // this is virtual template category (for Proj-CMS)
 					}
 				}
 
 				unset($vars['is_virtual']);
 			}
 
 			foreach ($vars as $name => $value) {
 				$this->HTTPQuery->Set($name, $value);
 			}
 
 			$this->InitAll(); // also will use parsed language to load phrases from it
 
 			$this->HTTPQuery->finalizeParsing($passed);
 		}
 
 		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;
 		}
 
 		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');
 		}
 
 		function parseRewriteURL($url)
 		{
 			$vars = Array ('pass' => Array ('m'));
 			$url_parts = $url ? explode('/', trim(mb_strtolower($url, 'UTF-8'), '/')) : Array ();
 
 			if (($this->HTTPQuery->Get('rewrite') == 'on') || !$url_parts) {
 				$this->_setDefaultValues($vars);
 			}
 
 			if (!$url_parts) {
 				$this->InitAll();
 				$vars['t'] = $this->HTTPQuery->getDefaultTemplate('');
 
 				return $vars;
 			}
 			else {
 				$vars['t'] = '';
 			}
 
 			$this->_parseLanguage($url_parts, $vars);
 			$this->_parseTheme($url_parts, $vars);
 
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_page>
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_page> (category-based section template)
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<template>/<module_item>
 			// http://site-url/<language>/<theme>/<category>[_<category_page>]/<module_item> (category-based detail template)
 			// http://site-url/<language>/<theme>/<rl_injections>/<category>[_<category_page>]/<rl_part> (customized url)
 
 			if ( $this->processRewriteListeners($url_parts, $vars) ) {
 				return $vars;
 			}
 
 			if ($this->_parsePhisycalTemplate($url_parts, $vars)) {
 				$this->_partsFound[] = 'parsePhisycalTemplate';
 			}
 
 			if (($this->_modulePrefix === false) && in_array('parseCategory', $this->_partsFound)) {
 				// no item found, but category found -> module index page
 				/*foreach ($this->Application->RewriteListeners as $prefix => $listener) {
 					// no idea what module we are talking about, so pass info form all modules
 					$vars['pass'][] = $prefix;
 				}*/
 
 				return $vars;
 			}
 
 			if (!$this->_partsFound) {
 				$not_found = $this->Application->ConfigValue('ErrorTemplate');
 				$vars['t'] = $not_found ? $not_found : 'error_notfound';
 
 				$themes_helper =& $this->Application->recallObject('ThemesHelper');
 				/* @var $themes_helper kThemesHelper */
 
 				$vars['m_cat_id'] = $themes_helper->getPageByTemplate($vars['t'], $vars['m_theme']);
 
 				header('HTTP/1.0 404 Not Found');
 			}
 
 			return $vars;
 		}
 
 		function InitAll()
 		{
 			$this->Application->VerifyThemeId();
 			$this->Application->VerifyLanguageId();
-			$this->Application->Phrases->Init('phrases');
+
+			// no need, since we don't have any cached phrase IDs + nobody will use PhrasesCache::LanguageId soon
+			// $this->Application->Phrases->Init('phrases');
 		}
 
 		/**
 		 * Processes url using rewrite listeners
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function processRewriteListeners(&$url_parts, &$vars)
 		{
 			$this->initRewriteListeners();
 
 			$page_number = $this->_parsePage($url_parts, $vars);
 
 			if ($page_number) {
 				$this->_partsFound[] = 'parsePage';
 			}
 
 			foreach ($this->Application->RewriteListeners as $prefix => $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;
 		}
 
 		/**
 		 * Parses real template name from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parsePhisycalTemplate($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			do {
 				$template_path = implode('/', $url_parts);
 
-				$physical_template = array_search($template_path, $this->Application->structureTemplateMapping);
+				$physical_template = $this->Application->getPhysicalTemplate($template_path);
 
 				if (($physical_template !== false) && (substr($physical_template, 0, 3) != 'id:')) {
 					// replace menu template name with it's actual template name on disk
 					list ($template_path) = explode(':', $physical_template, 2);
 				}
 
 				$t_parts['path'] = dirname($template_path) == '.' ? '' : '/' . dirname($template_path);
 				$t_parts['file'] = basename($template_path);
 
 				$sql = 'SELECT FileId
 						FROM ' . TABLE_PREFIX . 'ThemeFiles
 						WHERE (ThemeId = ' . $vars['m_theme'] . ') AND (FilePath = ' . $this->Conn->qstr($t_parts['path']) . ') AND (FileName = ' . $this->Conn->qstr($t_parts['file'] . '.tpl') . ')';
 				$template_found = $this->Conn->GetOne($sql);
 
 				if (!$template_found) {
 					array_shift($url_parts);
 				}
 			} while (!$template_found && $url_parts);
 
 			if ($template_found) {
 				$vars['t'] = $template_path;
 
 				// 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
 //				$themes_helper =& $this->Application->recallObject('ThemesHelper');
 //				/* @var $themes_helper kThemesHelper */
 //
 //				$vars['m_cat_id'] = $themes_helper->getPageByTemplate($template_path, $vars['m_theme']);
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Parses category part of url, build main part of url
 		 *
 		 * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE.
 		 * @param string $prefix Prefix, that listener uses for system integration
 		 * @param Array $params Params, that are used for url building or created during url parsing.
 		 * @param Array $url_parts Url parts to parse (only for parsing).
 		 * @param bool $keep_events Keep event names in resulting url (only for building).
 		 * @return bool|string|Array Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener.
 		 */
 		function MainRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false)
 		{
 			if ($rewrite_mode == REWRITE_MODE_BUILD) {
 				return $this->_buildMainUrl($prefix, $params, $keep_events);
 			}
 
 			if ( $this->_parseFriendlyUrl($url_parts, $params) ) {
 				// friendly urls work like exact match only!
 				return false;
 			}
 
 			if ($this->_parseCategory($url_parts, $params)) {
 				$this->_partsFound[] = 'parseCategory';
 			}
 
 			return true;
 		}
 
 		/**
 		 * Build main part of every url
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return string
 		 */
 		function _buildMainUrl($prefix_special, &$params, $keep_events)
 		{
 			$ret = '';
 			list ($prefix) = explode('.', $prefix_special);
 
 			$processed_params = $this->getProcessedParams($prefix_special, $params, $keep_events);
 			if ($processed_params === false) {
 				return '';
 			}
 
 			// add language
 			if (!$this->primaryLanguageId) {
 				// when domain-based language not found -> use site-wide language
 				$this->primaryLanguageId = $this->Application->GetDefaultLanguageId();
 			}
 
 			if ($processed_params['m_lang'] && ($processed_params['m_lang'] != $this->primaryLanguageId)) {
 				$language_name = $this->Application->getCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]');
 				if ($language_name === false) {
 					$sql = 'SELECT PackName
 							FROM ' . TABLE_PREFIX . 'Language
 							WHERE LanguageId = ' . $processed_params['m_lang'];
 					$language_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('language_names[%LangIDSerial:' . $processed_params['m_lang'] . '%]', $language_name);
 				}
 
 				$ret .= $language_name . '/';
 			}
 
 			// add theme
 			if (!$this->primaryThemeId) {
 				// when domain-based theme not found -> use site-wide theme
 				$this->primaryThemeId = $this->Application->GetDefaultThemeId(true);
 			}
 
 			if ($processed_params['m_theme'] && ($processed_params['m_theme'] != $this->primaryThemeId)) {
 				$theme_name = $this->Application->getCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]');
 				if ($theme_name === false) {
 					$sql = 'SELECT Name
 							FROM ' . TABLE_PREFIX . 'Theme
 							WHERE ThemeId = ' . $processed_params['m_theme'];
 					$theme_name = $this->Conn->GetOne($sql);
 
 					$this->Application->setCache('theme_names[%ThemeIDSerial:' . $processed_params['m_theme'] . '%]', $theme_name);
 
 				}
 
 				$ret .= $theme_name . '/';
 			}
 
 			// inject custom url parts made by other rewrite listeners just after language/theme url parts
 			if ($params['inject_parts']) {
 				$ret .= implode('/', $params['inject_parts']) . '/';
 			}
 
 			// add category
 			if ($processed_params['m_cat_id'] > 0 && $params['pass_category']) {
 				$category_filename = $this->Application->getCategoryCache($processed_params['m_cat_id'], 'filenames');
 
 				preg_match('/^Content\/(.*)/i', $category_filename, $regs);
 
 				if ($regs) {
 					$template = array_key_exists('t', $params) ? $params['t'] : false;
 
 					if (strtolower($regs[1]) == strtolower($template)) {
 						// we could have category path like "Content/<template_path>" in this case remove template
 						$params['pass_template'] = false;
 					}
 
 					$ret .= $regs[1] . '/';
 				}
 
 				$params['category_processed'] = true;
 			}
 
 			// reset category page
 			$force_page_adding = false;
 			if (array_key_exists('reset', $params) && $params['reset']) {
 				unset($params['reset']);
 
 				if ($processed_params['m_cat_id']) {
 					$processed_params['m_cat_page'] = 1;
 					$force_page_adding = true;
 				}
 			}
 
 			if ((array_key_exists('category_processed', $params) && $params['category_processed'] && ($processed_params['m_cat_page'] > 1)) || $force_page_adding) {
 				// category name was added before AND category page number found
 				$ret = rtrim($ret, '/') . '_' . $processed_params['m_cat_page'] . '/';
 			}
 
 			$template = array_key_exists('t', $params) ? $params['t'] : false;
 			$category_template = ($processed_params['m_cat_id'] > 0) && $params['pass_category'] ? $this->Application->getCategoryCache($processed_params['m_cat_id'], 'category_designs') : '';
 
 			if ((strtolower($template) == '__default__') && ($processed_params['m_cat_id'] == 0)) {
 				// for "Home" category set template to index when not set
 				$template = 'index';
 			}
 
 			// remove template from url if it is category index cached template
 			if (($template == $category_template) || (mb_strtolower($template) == '__default__')) {
 				// given template is also default template for this category or '__default__' given
 				$params['pass_template'] = false;
 			}
 
 			if ($template && $params['pass_template']) {
 				$ret .= $template . '/';
 			}
 
 			return mb_strtolower( rtrim($ret, '/') );
 		}
 
 		/**
 		 * Gets language part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parseLanguage(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$url_part = reset($url_parts);
 
 			$sql = 'SELECT LanguageId, IF(LOWER(PackName) = ' . $this->Conn->qstr($url_part) . ', 2, PrimaryLang) AS SortKey
 					FROM ' . TABLE_PREFIX . 'Language
 					WHERE Enabled = 1
 					ORDER BY SortKey DESC';
 			$language_info = $this->Conn->GetRow($sql);
 
 			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);
 				}
 				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
 		 */
 		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);
 				}
 				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;
 		}
 
 		/**
 		 * Checks if whole url_parts matches a whole In-CMS page
 		 *
 		 * @param array $url_parts
 		 * @return boolean
 		 */
 		function _parseFriendlyUrl($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$sql = 'SELECT CategoryId, NamedParentPath
 					FROM ' . TABLE_PREFIX . 'Category
 					WHERE FriendlyURL = ' . $this->Conn->qstr(implode('/', $url_parts));
 
 			$friendly = $this->Conn->GetRow($sql);
 			if ($friendly) {
 				$vars['m_cat_id'] = $friendly['CategoryId'];
 				$vars['t'] = preg_replace('/^Content\//i', '', $friendly['NamedParentPath']);
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Set's page (when found) to all modules
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return string
 		 *
 		 * @todo Should find a way, how to determine what rewrite listerner page is it
 		 */
 		function _parsePage(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$page_number = end($url_parts);
 			if (!is_numeric($page_number)) {
 				return false;
 			}
 
 			array_pop($url_parts);
 
 			return $page_number;
 		}
 
 		/**
 		 * Remove page numbers for all rewrite listeners
 		 *
 		 * @todo Should find a way, how to determine what rewrite listerner page is it
 		 */
 		function removePages()
 		{
 			/*foreach ($this->Application->RewriteListeners as $prefix => $listener) {
 				$this->Application->DeleteVar($prefix . '_Page');
 			}*/
 		}
 
 		/**
 		 * Extracts category part from url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool
 		 */
 		function _parseCategory($url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$res = false;
 			$url_part = array_shift($url_parts);
 
 			$category_id = 0;
 			$last_category_info = false;
 			$category_path = $url_part == 'content' ? '' : 'content';
 
 			do {
 				$category_path = trim($category_path . '/' . $url_part, '/');
 				// bb_<topic_id> -> forums/bb_2
 				if ( !preg_match('/^bb_[\d]+$/', $url_part) && preg_match('/(.*)_([\d]+)$/', $category_path, $rets) ) {
 					$category_path = $rets[1];
 					$vars['m_cat_page'] = $rets[2];
 				}
 
 				$sql = 'SELECT CategoryId, SymLinkCategoryId, NamedParentPath
 						FROM ' . TABLE_PREFIX . 'Category
 						WHERE Status IN (1,4) AND (LOWER(NamedParentPath) = ' . $this->Conn->qstr($category_path) . ') AND (ThemeId = ' . $vars['m_theme'] . ' OR ThemeId = 0)';
 				$category_info = $this->Conn->GetRow($sql);
 
 				if ($category_info !== false) {
 					$last_category_info = $category_info;
 					$url_part = array_shift($url_parts);
 					$res = true;
 				}
 			} while ($category_info !== false && $url_part);
 
 			if ($last_category_info) {
 				// this category is symlink to other category, so use it's url instead
 				// (used in case if url prior to symlink adding was indexed by spider or was bookmarked)
 				if ($last_category_info['SymLinkCategoryId']) {
 					$sql = 'SELECT CategoryId, NamedParentPath
 							FROM ' . TABLE_PREFIX . 'Category
 							WHERE (CategoryId = ' . $last_category_info['SymLinkCategoryId'] . ')';
 					$category_info = $this->Conn->GetRow($sql);
 
 					if ($category_info) {
 						// web symlinked category was found use it
 						// TODO: maybe 302 redirect should be made to symlinked category url (all other url parts should stay)
 						$last_category_info = $category_info;
 					}
 				}
 
 				// 1. Set virtual page as template, this will be replaced to physical template later in kApplication::Run.
 				// 2. Don't set CachedTemplate field as template here, because we will loose original page associated with it's cms blocks!
 				$vars['t'] = mb_strtolower( preg_replace('/^Content\//i', '', $last_category_info['NamedParentPath']), 'UTF-8' );
 
 				$vars['m_cat_id'] = $last_category_info['CategoryId'];
 				$vars['is_virtual'] = true; // for template from POST, strange code there!
 			}
 			else {
 				$vars['m_cat_id'] = 0;
 			}
 
 			return $res;
 		}
 
 		/**
 		 * Builds/parses category item part of url
 		 *
 		 * @param int $rewrite_mode Mode in what rewrite listener was called. Possbile two modes: REWRITE_MODE_BUILD, REWRITE_MODE_PARSE.
 		 * @param string $prefix Prefix, that listener uses for system integration
 		 * @param Array $params Params, that are used for url building or created during url parsing.
 		 * @param Array $url_parts Url parts to parse (only for parsing).
 		 * @param bool $keep_events Keep event names in resulting url (only for building).
 		 * @return bool Return true to continue to next listener; return false (when building) not to rewrite given prefix; return false (when parsing) to stop processing at this listener.
 		 */
 		function CategoryItemRewriteListener($rewrite_mode = REWRITE_MODE_BUILD, $prefix, &$params, &$url_parts, $keep_events = false)
 		{
 			static $parsed = false;
 
 			if ($rewrite_mode == REWRITE_MODE_BUILD) {
 				return $this->_buildCategoryItemUrl($prefix, $params, $keep_events);
 			}
 
 			if (!$parsed) {
 				$this->_modulePrefix = $this->_parseCategoryItemUrl($url_parts, $params);
 				if ($this->_modulePrefix !== false) {
 					$params['pass'][] = $this->_modulePrefix;
 					$this->_partsFound[] = 'parseCategoryItemUrl';
 				}
 
 				$parsed = true;
 			}
 
 			return true;
 		}
 
 		/**
 		 * Build category teim part of url
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return string
 		 */
 		function _buildCategoryItemUrl($prefix_special, &$params, $keep_events)
 		{
 			static $default_per_page = Array ();
 
 			$ret = '';
 			list ($prefix) = explode('.', $prefix_special);
 			$processed_params = $this->getProcessedParams($prefix_special, $params, $keep_events);
 
 			if ($processed_params === false) {
 				return '';
 			}
 
 			if (!array_key_exists($prefix, $default_per_page)) {
 				$list_helper =& $this->Application->recallObject('ListHelper');
 				/* @var $list_helper ListHelper */
 
 				$default_per_page[$prefix] = $list_helper->getDefaultPerPage($prefix);
 			}
 
 			if ($processed_params[$prefix_special . '_id']) {
 				$category_id = array_key_exists('m_cat_id', $params) ? $params['m_cat_id'] : $this->Application->GetVar('m_cat_id');
 
 				// if template is also item template of category, then remove template
 				$template = array_key_exists('t', $params) ? $params['t'] : false;
 				$item_template = $this->GetItemTemplate($category_id, $prefix);
 
 				if ($template == $item_template || strtolower($template) == '__default__') {
 					// given template is also default template for this category item or '__default__' given
 					$params['pass_template'] = false;
 				}
 
 				// get item's filename
 				if ($prefix == 'bb') {
 					$ret .= 'bb_' . $processed_params[$prefix_special . '_id'] . '/';
 				}
 				else {
-					$filename = $this->Application->getFilename($prefix, $processed_params[$prefix_special . '_id'], $category_id);
+					$filename = $this->getFilename($prefix, $processed_params[$prefix_special . '_id'], $category_id);
 					if ($filename !== false) {
 						$ret .= $filename . '/';
 					}
 				}
 			} else {
 				if ($processed_params[$prefix_special . '_Page'] == 1) {
 					// when printing category items and we are on the 1st page -> there is no information about
 					// category item prefix and $params['pass_category'] will not be added automatically
 					$params['pass_category'] = true;
 				}
 				elseif ($processed_params[$prefix_special . '_Page'] > 1) {
 					// $ret .= $processed_params[$prefix_special . '_Page'] . '/';
 					$params['page'] = $processed_params[$prefix_special . '_Page'];
 				}
 
 				$per_page = $processed_params[$prefix_special . '_PerPage'];
 
 				if ($per_page && ($per_page != $default_per_page[$prefix])) {
 					$params['per_page'] = $processed_params[$prefix_special . '_PerPage'];
 				}
 			}
 
 			return mb_strtolower( rtrim($ret, '/') );
 		}
 
 		/**
+		 * Returns item's filename that corresponds id passed. If possible, then get it from cache
+		 *
+		 * @param string $prefix
+		 * @param int $id
+		 * @param int $category_id
+		 * @return string
+		 */
+		function getFilename($prefix, $id, $category_id = null)
+		{
+			if ($prefix == 'c') {
+				throw new Exception('Method "<strong>' . __FUNCTION__ . '</strong>" no longer work with "<strong>c</strong>" prefix. Please use "<strong>getCategoryCache</strong>" method instead');
+
+				return false;
+			}
+
+			$category_id = isset($category_id) ? $category_id : $this->Application->GetVar('m_cat_id');
+
+			$cache_key = 'filenames[%' . $this->Application->incrementCacheSerial($prefix, $id, false) . '%]:' . (int)$category_id;
+			$filename = $this->Application->getCache($cache_key);
+
+			if ($filename === false) {
+				$this->Conn->nextQueryCachable = true;
+				$sql = 'SELECT ResourceId
+						FROM ' . $this->Application->getUnitOption($prefix, 'TableName') . '
+						WHERE ' . $this->Application->getUnitOption($prefix, 'IDField') . ' = ' . $this->Conn->qstr($id);
+				$resource_id = $this->Conn->GetOne($sql);
+
+				$this->Conn->nextQueryCachable = true;
+				$sql = 'SELECT Filename
+						FROM ' . TABLE_PREFIX . 'CategoryItems
+						WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')';
+				$filename = $this->Conn->GetOne($sql);
+
+				if ($filename !== false) {
+					$this->Application->setCache($cache_key, $filename);
+				}
+			}
+
+			return $filename;
+		}
+
+		/**
 		 * Sets template and id, corresponding to category item given in url
 		 *
 		 * @param Array $url_parts
 		 * @param Array $vars
 		 * @return bool|string
 		 */
 		function _parseCategoryItemUrl(&$url_parts, &$vars)
 		{
 			if (!$url_parts) {
 				return false;
 			}
 
 			$item_filename = end($url_parts);
 			if (is_numeric($item_filename)) {
 				// this page, don't process here
 				return false;
 			}
 
 			if (preg_match('/^bb_([\d]+)/', $item_filename, $regs)) {
 				// process topics separatly, because they don't use item filenames
 				array_pop($url_parts);
 
 				return $this->_parseTopicUrl($regs[1], $vars);
 			}
 
 			// locating the item in CategoryItems by filename to detect its ItemPrefix and its category ParentPath
 			$sql = 'SELECT ci.ItemResourceId, ci.ItemPrefix, c.ParentPath, ci.CategoryId
 					FROM ' . TABLE_PREFIX . 'CategoryItems AS ci
 					LEFT JOIN ' . TABLE_PREFIX . 'Category AS c ON c.CategoryId = ci.CategoryId
 					WHERE (ci.CategoryId = ' . (int)$vars['m_cat_id'] . ') AND (ci.Filename = ' . $this->Conn->qstr($item_filename) . ')';
 			$cat_item = $this->Conn->GetRow($sql);
 
 			if ($cat_item !== false) {
 				// item found
 				$module_prefix = $cat_item['ItemPrefix'];
 				$item_template = $this->GetItemTemplate($cat_item, $module_prefix);
 
 				// converting ResourceId to correpsonding Item id
 				$module_config = $this->Application->getUnitOptions($module_prefix);
 
 				$sql = 'SELECT ' . $module_config['IDField'] . '
 						FROM ' . $module_config['TableName'] . '
 					 	WHERE ResourceId = ' . $cat_item['ItemResourceId'];
 				$item_id = $this->Conn->GetOne($sql);
 
 				array_pop($url_parts);
 
 				if ($item_id) {
 					if ($item_template) {
 						// when template is found in category -> set it
 						$vars['t'] = $item_template;
 					}
 
 					// we have category item id
 					$vars[$module_prefix . '_id'] = $item_id;
 
 					return $module_prefix;
 				}
 			}
 
 			return false;
 		}
 
 		/**
 		 * Set's template and topic id corresponding to topic given in url
 		 *
 		 * @param int $topic_id
 		 * @param Array $vars
 		 * @return string
 		 */
 		function _parseTopicUrl($topic_id, &$vars)
 		{
 			$sql = 'SELECT c.ParentPath, c.CategoryId
 					FROM ' . TABLE_PREFIX . 'Category AS c
 					WHERE c.CategoryId = ' . (int)$vars['m_cat_id'];
 			$cat_item = $this->Conn->GetRow($sql);
 
 			$item_template = $this->GetItemTemplate($cat_item, 'bb');
 
 			if ($item_template) {
 				$vars['t'] = $item_template;
 			}
 
 			$vars['bb_id'] = $topic_id;
 
 			return 'bb';
 		}
 
 		/**
 		 * Returns enviroment variable values for given prefix (uses directly given params, when available)
 		 *
 		 * @param string $prefix_special
 		 * @param Array $params
 		 * @param bool $keep_events
 		 * @return Array
 		 */
 		function getProcessedParams($prefix_special, &$params, $keep_events)
 		{
 			list ($prefix) = explode('.', $prefix_special);
 
 			$query_vars = $this->Application->getUnitOption($prefix, 'QueryString');
 			if (!$query_vars) {
 				// given prefix doesn't use "env" variable to pass it's data
 				return false;
 			}
 
 			$event_key = array_search('event', $query_vars);
 			if ($event_key) {
 				// pass through event of this prefix
 				unset($query_vars[$event_key]);
 			}
 
 			if (array_key_exists($prefix_special . '_event', $params) && !$params[$prefix_special . '_event']) {
 				// if empty event, then remove it from url
 				unset( $params[$prefix_special . '_event'] );
 			}
 
 			// if pass events is off and event is not implicity passed
 			if (!$keep_events && !array_key_exists($prefix_special . '_event', $params)) {
 				unset($params[$prefix_special . '_event']); // remove event from url if requested
 				//otherwise it will use value from get_var
 			}
 
 			$processed_params = Array ();
 			foreach ($query_vars as $index => $var_name) {
 				// if value passed in params use it, otherwise use current from application
 				$var_name = $prefix_special . '_' . $var_name;
 				$processed_params[$var_name] = array_key_exists($var_name, $params) ? $params[$var_name] : $this->Application->GetVar($var_name);
 				if (array_key_exists($var_name, $params)) {
 					unset($params[$var_name]);
 				}
 			}
 
 			return $processed_params;
 		}
 
 		/**
 		 * Returns module item details template specified in given category custom field for given module prefix
 		 *
 		 * @param int|Array $category
 		 * @param string $module_prefix
 		 * @return string
 		 */
 		function GetItemTemplate($category, $module_prefix)
 		{
 			$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 ($item_template && array_key_exists($item_template, $this->_templateAliases)) {
 				$item_template = $this->_templateAliases[$item_template];
 			}
 
 			$this->Application->setCache($cache_key, $item_template);
 
 			return $item_template;
 		}
 
 		/**
 		 * Loads all registered rewrite listeners, so they could be quickly accessed later
 		 *
 		 */
 		function initRewriteListeners()
 		{
 			static $init_done = false;
 
 			if ($init_done || count($this->Application->RewriteListeners) == 0) {
 				// not inited 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);
 				}
 			}
 
 			define('MOD_REWRITE_URL_ENDING', $this->Application->ConfigValue('ModRewriteUrlEnding'));
 
 			$init_done = true;
 		}
 
 		/**
 		 * Returns category custom field id, where given module prefix item template name is stored
 		 *
 		 * @param string $module_prefix
 		 * @return int
 		 */
 		function getItemTemplateCustomField($module_prefix)
 		{
 			$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;
 		}
 
 		/**
 		 * Sets default parsed values before actual url parsing (only, for empty url)
 		 *
 		 * @param Array $vars
 		 */
 		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;
 				}
 			}
 		}
 	}
Index: branches/5.2.x/core/units/configuration/configuration_event_handler.php
===================================================================
--- branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 14183)
+++ branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 14184)
@@ -1,291 +1,291 @@
 <?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
 		 */
 		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
 		 * @access protected
 		 * @see OnListBuild
 		 */
 		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 <> ""');
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemUpdate(&$event)
 		{
 			static $default_field_options = null;
 
 			$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');
 					$object->SetDBField('VariableValue', $password_formatter->EncryptPassword($object->GetDBField('VariableValue'), 'b38'));
 				}
 			}
 
 			$field_values = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 
 			$state_country_hash = Array (
 				'Comm_State' => 'Comm_Country',
 				'Comm_Shipping_State' => 'Comm_Shipping_Country'
 			);
 
 			$field_name = $object->GetDBField('VariableName');
 			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 true;
 				}
 
 				$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_NotAllowed');
 				}
 
 			}
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			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'
 			);
 
 			if (in_array($variable_name, $watch_variables)) {
 				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 
 				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;
 				}
 			}
 
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdate(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = kEvent::erFAIL;
 				return ;
 			}
 
 			$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) {
 				unset($items_info['ModuleRootCategory']);
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			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);
 
 					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) {
 					// 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) {
 						// 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));
 					}
 				}
 
 				// reset cache
 				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 				$require_refresh = Array (
 					'AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface'
 				);
 
 				$refresh_sections = array_intersect($require_refresh, $changed);
 				$require_full_refresh = Array ('Site_Name', 'AdminConsoleInterface');
 
 				if (array_intersect($require_full_refresh, $changed)) {
 					$event->SetRedirectParam('refresh_all', 1);
 				} elseif ($refresh_sections) {
 					// reset sections too, because of AdvancedUserManagement
 					$event->SetRedirectParam('refresh_tree', 1);
 				}
 
-				$this->Application->UnitConfigReader->ResetParsedData($refresh_sections ? true : false);
+				$this->Application->DeleteUnitCache($refresh_sections ? true : false);
 			}
 			elseif ($this->Application->GetVar('errors_' . $event->getPrefixSpecial())) {
 				// because we have list out there, and this is item
 				$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 14183)
+++ branches/5.2.x/core/units/modules/modules_event_handler.php	(revision 14184)
@@ -1,196 +1,196 @@
 <?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);
 		}
 
 		/**
 		 * List with one record if special passed
 		 *
 		 * @param kEvent $event
 		 */
 		function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			if ($event->Special) {
 				$object->addFilter('current_module', '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) {
 				$event->status = kEvent::erSUCCESS;
 				$event->setRedirectParams(Array ('opener' => 's'), true);
 
-				$this->Application->UnitConfigReader->ResetParsedData(true); //true to reset sections cache also
+				$this->Application->DeleteUnitCache(true); //true to reset sections cache also
 				$event->SetRedirectParam('RefreshTree', 1);
 			}
 		}
 
 		/**
 		 * Occures after list is queried
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterListQuery(&$event)
 		{
 			parent::OnAfterListQuery($event);
 
 			$new_modules = $this->_getNewModules();
 			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);
 			}
 		}
 
 		/**
 		 * Returns list of modules, that are not installed, but available in file system
 		 *
 		 * @return Array
 		 */
 		function _getNewModules()
 		{
 			$modules_helper =& $this->Application->recallObject('ModulesHelper');
 			/* @var $modules_helper kModulesHelper */
 
 			$modules = Array ();
 			if ($dir = @opendir(MODULES_PATH)) {
 				while (($file = readdir($dir)) !== false) {
 					if ($file != '.' && $file != '..') {
 						$module_folder = MODULES_PATH . '/' . $file;
 						if (is_dir($module_folder) && $this->_isModule($module_folder)) {
 							// this is module -> check if it's installed already
 							if (!$modules_helper->moduleInstalled($file)) {
 								$install_order = trim( file_get_contents($module_folder . '/install/install_order.txt') );
 								$modules[$install_order] = $file;
 							}
 						}
 					}
 				}
 
 				closedir($dir);
 			}
 
 			// allows to control module install order
 			ksort($modules, SORT_NUMERIC);
 			return $modules;
 		}
 
 		/**
 		 * Checks, that given folder is module root folder
 		 *
 		 * @param string $folder
 		 * @return bool
 		 */
 		function _isModule($folder)
 		{
 			return file_exists($folder . '/install.php') && file_exists($folder . '/install/install_schema.sql');
 		}
 	}
\ No newline at end of file
Index: branches/5.2.x/core/units/admin/admin_events_handler.php
===================================================================
--- branches/5.2.x/core/units/admin/admin_events_handler.php	(revision 14183)
+++ branches/5.2.x/core/units/admin/admin_events_handler.php	(revision 14184)
@@ -1,1377 +1,1380 @@
 <?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 AdminEventsHandler extends kDBEventHandler {
 
 	function mapPermissions()
 	{
 		parent::mapPermissions();
 		$permissions = Array(
 			'OnSaveColumns'		=>	array('self' => true),
 			'OnClosePopup'		=>	array('self' => true),
 			'OnSaveSetting'		=>	array('self' => true),
 			// export/import permissions is checked within events
 			'OnExportCSV'		=>	Array('self' => true),
 			'OnGetCSV'			=>	Array('self' => true),
 			'OnCSVImportBegin'	=>	Array('self' => true),
 			'OnCSVImportStep'	=>	Array('self' => true),
 			'OnDropTempTablesByWID' => Array('self' => true),
 		);
 		$this->permMapping = array_merge($this->permMapping, $permissions);
 	}
 
 	/**
 	 * Checks permissions of user
 	 *
 	 * @param kEvent $event
 	 */
 	function CheckPermission(&$event)
 	{
 		$perm_value = null;
 
 		$system_events = Array (
 			'OnResetModRwCache', 'OnResetSections', 'OnResetConfigsCache', 'OnResetParsedData', 'OnResetMemcache',
 			'OnDeleteCompiledTemplates', 'OnCompileTemplates', 'OnGenerateTableStructure',
 			'OnRebuildThemes', 'OnCheckPrefixConfig', 'OnMemoryCacheGet', 'OnMemoryCacheSet'
 		);
 
 		if (in_array($event->Name, $system_events)) {
 			// events from "Tools -> System Tools" section are controlled via that section "edit" permission
 			$perm_value = /*$this->Application->isDebugMode() ||*/ $this->Application->CheckPermission($event->getSection() . '.edit');
 		}
 
 		$tools_events = Array (
 			'OnBackup' => 'in-portal:backup.view',
 			'OnBackupProgress' => 'in-portal:backup.view',
 			'OnDeleteBackup' => 'in-portal:backup.view',
 			'OnBackupCancel' => 'in-portal:backup.view',
 
 			'OnRestore' => 'in-portal:restore.view',
 			'OnRestoreProgress' => 'in-portal:restore.view',
 			'OnRestoreCancel' => 'in-portal:backup.view',
 
 			'OnSqlQuery' => 'in-portal:sql_query.view',
 		);
 
 		if (array_key_exists($event->Name, $tools_events)) {
 			$perm_value = $this->Application->CheckPermission($tools_events[$event->Name]);
 		}
 
 		if ($event->Name == 'OnSaveMenuFrameWidth') {
 			$perm_value = $this->Application->isAdminUser;
 		}
 
 		if (isset($perm_value)) {
 			$perm_helper =& $this->Application->recallObject('PermissionsHelper');
 			/* @var $perm_helper kPermissionsHelper */
 
 			return $perm_helper->finalizePermissionCheck($event, $perm_value);
 		}
 
 		return parent::CheckPermission($event);
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 	function OnResetModRwCache(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
 		$this->Conn->Query('DELETE FROM ' . TABLE_PREFIX . 'CachedUrls');
 	}
 
 	function OnResetSections(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$this->Application->deleteCache('master:sections_parsed');
 		}
 		else {
 			$this->Application->deleteDBCache('sections_parsed');
 		}
 
 		$event->SetRedirectParam('refresh_tree', 1);
 	}
 
 	function OnResetConfigsCache(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
 		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
 			$this->Application->deleteCache('master:config_files');
 		}
 		else {
 			$this->Application->deleteDBCache('config_files');
 		}
 
 		$this->OnResetParsedData($event);
 
 		$skin_helper =& $this->Application->recallObject('SkinHelper');
 		/* @var $skin_helper SkinHelper */
 
 		$skin_helper->deleteCompiled();
 	}
 
 	/**
 	 * Resets parsed data from unit configs
 	 *
 	 * @param kEvent $event
 	 */
 	function OnResetParsedData(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = erSTOP;
 		}
 
-		$this->Application->UnitConfigReader->ResetParsedData();
+		$this->Application->DeleteUnitCache();
 
 		if ( $this->Application->GetVar('validate_configs') ) {
 			$event->SetRedirectParam('validate_configs', 1);
 		}
 	}
 
 	function OnResetMemcache(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
-		if ($this->Application->isCachingType(CACHING_TYPE_MEMORY)) {
-			$this->Application->memoryCache->reset();
-		}
+		$this->Application->resetCache();
 	}
 
 	function OnCompileTemplates(&$event)
 	{
 		$compiler =& $this->Application->recallObject('NParserCompiler');
 		/* @var $compiler NParserCompiler */
 
 		$compiler->CompileTemplatesStep();
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Deletes all compiled templates
 	 *
 	 * @param kEvent $event
 	 */
 	function OnDeleteCompiledTemplates(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
 		$base_path = WRITEABLE . DIRECTORY_SEPARATOR . 'cache';
 
 		// delete debugger reports
 		$debugger_reports = glob($base_path . '/debug_@*@.txt');
 
 		foreach ($debugger_reports as $debugger_report) {
 			unlink($debugger_report);
 		}
 
 		$this->_deleteCompiledTemplates($base_path);
 	}
 
 	function _deleteCompiledTemplates($folder, $unlink_folder = false)
 	{
 		$sub_folders = glob($folder . '/*', GLOB_ONLYDIR);
 
 		foreach ($sub_folders as $sub_folder) {
 			$this->_deleteCompiledTemplates($sub_folder, true);
 		}
 
 		$files = glob($folder . '/*.php');
 
 		foreach ($files as $file) {
 			unlink($file);
 		}
 
 		if ($unlink_folder) {
 			rmdir($folder);
 		}
 	}
 
 	/**
 	 * Generates sturcture for specified table
 	 *
 	 * @param kEvent $event
 	 * @author Alex
 	 */
 	function OnGenerateTableStructure(&$event)
 	{
 		$types_hash = Array(
 			'string'	=>	'varchar|text|mediumtext|longtext|date|datetime|time|timestamp|char|year|enum|set',
 			'int'		=>	'smallint|mediumint|int|bigint|tinyint',
 			'float'		=>	'float|double|decimal',
 		);
 
 		$table_name = $this->Application->GetVar('table_name');
 		if (!$table_name) {
 			echo 'error: no table name specified';
 			return ;
 		}
 
 		if (TABLE_PREFIX && !preg_match('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', $table_name) && (strtolower($table_name) != $table_name)) {
 			// table name without prefix, then add it (don't affect K3 tables named in lowercase)
 			$table_name = TABLE_PREFIX.$table_name;
 		}
 
 		if (!$this->Conn->TableFound($table_name)) {
 			// table with prefix doesn't exist, assume that just config prefix passed -> resolve table name from it
 			$prefix = preg_replace('/^' . preg_quote(TABLE_PREFIX, '/') . '/', '', $table_name);
 			if ($this->Application->prefixRegistred($prefix)) {
 				// when prefix is found -> use it's table (don't affect K3 tables named in lowecase)
 				$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			}
 		}
 
 		$table_info = $this->Conn->Query('DESCRIBE '.$table_name);
 
 		// 1. prepare config keys
 		$grids = Array (
 			'Default' => Array (
 				'Icons' => Array ('default' => 'icon16_item.png'),
 				'Fields' => Array (),
 			)
 		);
 
 		$grid_fields = Array();
 
 		$id_field = '';
 		$fields = Array();
 		$float_types = Array ('float', 'double', 'numeric');
 		foreach ($table_info as $field_info) {
 			if (preg_match('/l[\d]+_.*/', $field_info['Field'])) {
 				// don't put multilingual fields in config
 				continue;
 			}
 
 			$field_options = Array ();
 			$grid_col_options = Array(
 				'title' => 'la_col_' . $field_info['Field'],
 				'filter_block' => 'grid_like_filter',
 			);
 
 			// 1. get php field type by mysql field type
 			foreach ($types_hash as $php_type => $db_types) {
 				if (preg_match('/'.$db_types.'/', $field_info['Type'])) {
 					$field_options['type'] = $php_type;
 					break;
 				}
 			}
 
 			// 2. get field default value
 			$default_value = $field_info['Default'];
 			$not_null = $field_info['Null'] != 'YES';
 
 			if (is_numeric($default_value)) {
 				$default_value = preg_match('/[\.,]/', $default_value) ? (float)$default_value : (int)$default_value;
 			}
 
 			if ( is_null($default_value) && $not_null ) {
 				$default_value = $field_options['type'] == 'string' ? '' : 0;
 			}
 
 			if ( in_array($php_type, $float_types) ) {
 				// this is float number
 				if (preg_match('/'.$db_types.'\([\d]+,([\d]+)\)/i', $field_info['Type'], $regs)) {
 					// size is described in structure -> add formatter
 					$field_options['formatter'] = 'kFormatter';
 					$field_options['format'] = '%01.'.$regs[1].'f';
 
 					if ($not_null) {
 						// null fields, will most likely have NULL as default value
 						$default_value = 0;
 					}
 				}
 				elseif ($not_null) {
 					// no size information, just convert to float
 					// null fields, will most likely have NULL as default value
 					$default_value = (float)$default_value;
 
 				}
 			}
 
 			if (preg_match('/varchar\(([\d]+)\)/i', $field_info['Type'], $regs)) {
 				$field_options['max_len'] = (int)$regs[1];
 			}
 
 			if (preg_match('/tinyint\([\d]+\)/i', $field_info['Type'])) {
 				$field_options['formatter'] = 'kOptionsFormatter';
 				$field_options['options'] = Array (1 => 'la_Yes', 0 => 'la_No');
 				$field_options['use_phrases'] = 1;
 				$grid_col_options['filter_block'] = 'grid_options_filter';
 			}
 
 			if ($not_null) {
 				$field_options['not_null'] = 1;
 			}
 
 			if ($field_info['Key'] == 'PRI') {
 				$default_value = 0;
 				$id_field = $field_info['Field'];
 			}
 
 			if ($php_type == 'int' && !$not_null) {
 				// numeric null field
 				if (preg_match('/(On|Date)$/', $field_info['Field']) || $field_info['Field'] == 'Modified') {
 					$field_options['formatter'] = 'kDateFormatter';
 					$grid_col_options['filter_block'] = 'grid_date_rage_filter';
 				}
 			}
 
 			if ($php_type == 'int' && ($not_null || is_numeric($default_value))) {
 				// is integer field AND not null
 				$field_options['default'] = (int)$default_value;
 			}
 			else {
 				$field_options['default'] = $default_value;
 			}
 
 			$fields[ $field_info['Field'] ] = $field_options;
 			$grids_fields[ $field_info['Field'] ] = $grid_col_options;
 		}
 
 		$grids['Default']['Fields'] = $grids_fields;
 
 		$ret = Array (
 			'IDField' => $id_field,
 			'Fields' => $fields,
 			'Grids' => $grids,
 		);
 
 		$decorator = new UnitConfigDecorator();
 		$ret = $decorator->decorate($ret);
 
 		$this->Application->InitParser();
 		ob_start();
 		echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
 	?>
 		<script type="text/javascript">
 			set_window_title('Table "<?php echo $table_name; ?>" Structure');
 		</script>
 
 		<a href="javascript:window_close();">Close Window</a><br /><br />
 		<?php echo $GLOBALS['debugger']->highlightString($ret); ?>
 		<br /><br /><a href="javascript:window_close();">Close Window</a><br />
 	<?php
 		echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
 		echo ob_get_clean();
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Refreshes ThemeFiles & Theme tables by actual content on HDD
 	 *
 	 * @param kEvent $event
 	 */
 	function OnRebuildThemes(&$event)
 	{
 		if ($this->Application->GetVar('ajax') == 'yes') {
 			$event->status = kEvent::erSTOP;
 		}
 
 		$themes_helper =& $this->Application->recallObject('ThemesHelper');
 		/* @var $themes_helper kThemesHelper */
 
 		$themes_helper->refreshThemes();
 	}
 
 	function OnSaveColumns(&$event)
 	{
 		$picker_helper =& $this->Application->recallObject('ColumnPickerHelper');
 		/* @var $picker_helper kColumnPickerHelper */
 
 		$picker_helper->SetGridName($this->Application->GetLinkedVar('grid_name'));
 
 		$picked = trim($this->Application->GetVar('picked_str'), '|');
 		$hidden = trim($this->Application->GetVar('hidden_str'), '|');
 
 		$main_prefix = $this->Application->GetVar('main_prefix');
 
 		$picker_helper->SaveColumns($main_prefix, $picked, $hidden);
 		$this->finalizePopup($event);
 	}
 
 	/**
 	 * Saves various admin settings via ajax
 	 *
 	 * @param kEvent $event
 	 */
 	function OnSaveSetting(&$event)
 	{
 		if ($this->Application->GetVar('ajax') != 'yes') {
 			return ;
 		}
 
 		$var_name = $this->Application->GetVar('var_name');
 		$var_value = $this->Application->GetVar('var_value');
 
 		$this->Application->StorePersistentVar($var_name, $var_value);
 
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Just closes popup & deletes last_template & opener_stack if popup, that is closing
 	 *
 	 * @param kEvent $event
 	 */
 	function OnClosePopup(&$event)
 	{
 		$event->SetRedirectParam('opener', 'u');
 	}
 
 	/**
 	 * Occurs right after initialization of the kernel, used mainly as hook-to event
 	 *
 	 * @param kEvent $event
 	 */
 	function OnStartup(&$event)
 	{
 
 	}
 
 	/**
 	 * Occurs right before echoing the output, in Done method of application, used mainly as hook-to event
 	 *
 	 * @param kEvent $event
 	 */
 	function OnBeforeShutdown(&$event)
 	{
 	}
 
 	/**
 	 * Is called after tree was build (when not from cache)
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterBuildTree(&$event)
 	{
 
 	}
 
 	/**
 	 * Called by AJAX to perform CSV export
 	 *
 	 * @param kEvent $event
 	 */
 	function OnExportCSV(&$event)
 	{
 		$export_helper =& $this->Application->recallObject('CSVHelper');
 		/* @var $export_helper kCSVHelper */
 
 		$prefix_special = $this->Application->GetVar('PrefixSpecial');
 		if(!$prefix_special) {
 			$prefix_special = $export_helper->ExportData('prefix');
 		}
 		$prefix_elems = split('\.|_', $prefix_special, 2);
 		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
 
 		if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
 			$event->status = kEvent::erPERM_FAIL;
 			return ;
 		}
 
 		$export_helper->PrefixSpecial = $prefix_special;
 		$export_helper->grid = $this->Application->GetVar('grid');
 		$export_helper->ExportStep();
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Returning created by AJAX CSV file
 	 *
 	 * @param kEvent $event
 	 */
 	function OnGetCSV(&$event)
 	{
 		$export_helper =& $this->Application->recallObject('CSVHelper');
 		/* @var $export_helper kCSVHelper */
 
 		$prefix_special = $export_helper->ExportData('prefix');
 		$prefix_elems = split('\.|_', $prefix_special, 2);
 		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
 
 		if(!$this->Application->CheckPermission($perm_sections['main'].'.view')) {
 			$event->status = kEvent::erPERM_FAIL;
 			return ;
 		}
 
 		$export_helper->GetCSV();
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 	function OnCSVImportBegin(&$event)
 	{
 		$prefix_special = $this->Application->GetVar('PrefixSpecial');
 		$prefix_elems = split('\.|_', $prefix_special, 2);
 		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
 
 		if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
 			$event->status = kEvent::erPERM_FAIL;
 			return ;
 		}
 
 		$object =& $event->getObject( Array('skip_autoload' => true) );
 		/* @var $object kDBItem */
 		$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 		$field_values = array_shift($items_info);
 		$object->SetFieldsFromHash($field_values);
 
 		$event->redirect = false;
 		$result = 'required';
 		if($object->GetDBField('ImportFile')) {
 			$import_helper =& $this->Application->recallObject('CSVHelper');
 			/* @var $import_helper kCSVHelper */
 			$import_helper->PrefixSpecial = $this->Application->GetVar('PrefixSpecial');
 			$import_helper->grid = $this->Application->GetVar('grid');
 			$result = $import_helper->ImportStart( $object->GetField('ImportFile', 'file_paths') );
 			if($result === true) {
 				$event->redirect = $this->Application->GetVar('next_template');
 				$event->SetRedirectParam('PrefixSpecial', $this->Application->GetVar('PrefixSpecial'));
 				$event->SetRedirectParam('grid', $this->Application->GetVar('grid'));
 			}
 		}
 
 		if($event->redirect === false) {
 			$object->SetError('ImportFile', $result);
 			$event->status = kEvent::erFAIL;
 		}
 	}
 
 	/**
 	 * Enter description here...
 	 *
 	 * @param kEvent $event
 	 */
 	function OnCSVImportStep(&$event)
 	{
 		$import_helper =& $this->Application->recallObject('CSVHelper');
 		/* @var $export_helper kCSVHelper */
 
 		$prefix_special = $import_helper->ImportData('prefix');
 		$prefix_elems = split('\.|_', $prefix_special, 2);
 		$perm_sections = $this->Application->getUnitOption($prefix_elems[0], 'PermSection');
 		if(!$this->Application->CheckPermission($perm_sections['main'].'.add') && !$this->Application->CheckPermission($perm_sections['main'].'.edit')) {
 			$event->status = kEvent::erPERM_FAIL;
 			return ;
 		}
 
 		$import_helper->ImportStep();
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Shows unit config filename, where requested prefix is defined
 	 *
 	 * @param kEvent $event
 	 */
 	function OnCheckPrefixConfig(&$event)
 	{
 		$prefix = $this->Application->GetVar('config_prefix');
 		$config_file = $this->Application->UnitConfigReader->prefixFiles[$prefix];
 
 		$this->Application->InitParser();
 
 		ob_start();
 		echo $this->Application->ParseBlock(Array('name' => 'incs/header', 'body_properties' => 'style="background-color: #E7E7E7; margin: 8px;"'));
 		?>
 		<script type="text/javascript">
 			set_window_title('Unit Config of "<?php echo $prefix; ?>" prefix');
 		</script>
 
 		<a href="javascript:window_close();">Close Window</a><br /><br />
 		<strong>Prefix:</strong> <?php echo $prefix; ?><br />
 		<strong>Unit Config:</strong> <?php echo $GLOBALS['debugger']->highlightString($config_file); ?><br />
 		<br /><a href="javascript:window_close();">Close Window</a><br />
 
 		<?php
 		echo $this->Application->ParseBlock(Array('name' => 'incs/footer'));
 		echo ob_get_clean();
 
 		$event->status = kEvent::erSTOP;
 	}
 
 	function OnDropTempTablesByWID(&$event)
 	{
 		$sid = $this->Application->GetSID();
 		$wid = $this->Application->GetVar('m_wid');
 		$tables = $this->Conn->GetCol('SHOW TABLES');
 		$mask_edit_table = '/'.TABLE_PREFIX.'ses_'.$sid.'_'.$wid.'_edit_(.*)$/';
 		foreach($tables as $table)
 		{
 			if( preg_match($mask_edit_table,$table,$rets) )
 			{
 				$this->Conn->Query('DROP TABLE IF EXISTS '.$table);
 			}
 		}
 		echo 'OK';
 		$event->status = kEvent::erSTOP;
 		return ;
 	}
 
 
 	/**
 	 * Backup all data
 	 *
 	 * @param kEvent $event
 	 */
 	function OnBackup(&$event)
 	{
 		$backup_path = $this->Application->ConfigValue('Backup_Path');
 
 		$file_helper =& $this->Application->recallObject('FileHelper');
 		/* @var $file_helper FileHelper */
 
 		if (!$file_helper->CheckFolder($backup_path) || !is_writable($backup_path)) {
 			$event->status = kEvent::erFAIL;
 
 			$this->Application->SetVar('error_msg', $this->Application->Phrase('la_Text_backup_access'));
 			return ;
 		}
 
 		$a_tables = $this->Conn->GetCol('SHOW TABLES'); //  array_keys($tables);
 		$TableNames = Array();
 		for($x=0;$x<count($a_tables);$x++)
 		{
 			if(substr($a_tables[$x],0,strlen(TABLE_PREFIX))==TABLE_PREFIX)
 		  	{
 		  	  	if (!strstr($a_tables[$x], 'ses_')) {
 		  			$TableNames[] = $a_tables[$x];
 		  	  	}
 		  	}
 		}
 
 		$backupProgress = Array (
 			'table_num' => 0,
 			'table_names' => $TableNames,
 			'table_count' => count($TableNames),
 			'record_count' => 0,
 			'file_name' => $backup_path."/dump".adodb_mktime().".txt",
 		);
 		$this->Application->RemoveVar('adm.backupcomplete_filename');
 		$this->Application->RemoveVar('adm.backupcomplete_filesize');
 
 		$out = array();
 
 		for($x=0;$x<count($TableNames);$x++) {
 			if (!strstr($TableNames[$x], 'ses_')) {
 				$out[] = $this->GetTableCreate($TableNames[$x]);
 			}
 		}
 
 		$fp = fopen($backupProgress['file_name'], 'a');
 
 		$sql = "SELECT Name, Version FROM ".TABLE_PREFIX."Modules";
 		$r = $this->Conn->Query($sql);
 		foreach ($r AS $a_module) {
 			$version = $a_module['Version'];
 			fwrite($fp, "# ".$a_module['Name']." Version: $version;\n");
 		}
 		fwrite($fp, "#------------------------------------------\n\n");
 
 		fwrite($fp,implode("\n",$out));
 		fwrite($fp,"\n");
 
 		fclose($fp);
 
 		$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
 		$event->redirect = 'tools/backup2';
 	}
 
 	/**
 	 * Perform next backup step
 	 *
 	 * @param kEvent $event
 	 */
 	function OnBackupProgress(&$event)
 	{
 		$done_percent = $this->performBackup();
 
 		if ($done_percent == 100) {
 			$event->redirect = 'tools/backup3';
 			return ;
 		}
 
 		echo $done_percent;
 		$event->status = kEvent::erSTOP;
 	}
 
 	/**
 	 * Stops Backup & redirect to Backup template
 	 *
 	 * @param kEvent $event
 	 */
 	function OnBackupCancel(&$event)
 	{
 		$event->redirect = 'tools/backup1';
 	}
 
 	/**
 	 * Stops Restore & redirect to Restore template
 	 *
 	 * @param kEvent $event
 	 */
 	function OnRestoreCancel(&$event)
 	{
 		$event->redirect = 'tools/restore1';
 	}
 
 	function performBackup()
 	{
 		$backupProgress = unserialize($this->Application->RecallVar('adm.backup_status'));
 //			echo "<pre>"; print_r($backupProgress); echo "</pre>";
 //			exit;
 		$CurrentTable = $backupProgress['table_names'][$backupProgress['table_num']];
 
 		// get records
 		$a_records = $this->insert_data($CurrentTable,$backupProgress['record_count'],50,"");
 //			echo "<pre>"; print_r($a_records); echo "</pre>";
 //			exit;
 
 		if ($a_records['num'] < 50) {
 			$backupProgress['table_num']++;
 //			if ($backupProgress['table_names'][$backupProgress['table_num']] == TABLE_PREFIX.'Cache') {
 //				$backupProgress['table_num']++;
 //			}
 			$backupProgress['record_count'] = 0;
 		} else {
 			$backupProgress['record_count']+=50;
 		}
 
 		if ($a_records['sql']) {
 			$fp = fopen($backupProgress['file_name'], 'a');
 			fwrite($fp, $a_records['sql']);
 			fclose($fp);
 		}
 		$percent = ($backupProgress['table_num'] / $backupProgress['table_count']) * 100;
 		if  ($percent >= 100) {
 			$percent = 100;
 			$this->Application->StoreVar('adm.backupcomplete_filename', $backupProgress['file_name']);
 			$this->Application->StoreVar('adm.backupcomplete_filesize', round(filesize($backupProgress['file_name'])/1024/1024, 2)); // Mbytes
 		} else {
 			$this->Application->StoreVar('adm.backup_status', serialize($backupProgress));
 		}
 
 		return round($percent);
 
 	}
 
 	//extracts the rows of data from tables using limits
 	function insert_data($table, $start, $limit, $mywhere)
 	{
 //			global $out;
 
 	    if ($mywhere !="")
 	    {
 	        $whereclause= " WHERE ".$mywhere." ";
 	    }
 	    else
 	    {
 	        $whereclause = "";
 	    }
 
 	    $a_data = $this->Conn->Query("SELECT * from $table $whereclause LIMIT $start, $limit");
 //			echo "SELECT * from $table $whereclause LIMIT $start, $limit";
 //			echo "<pre>"; print_r($a_records); echo "</pre>";
 //			exit;
 	    if (!$a_data) {
 	    	return Array(
 	    		'num' => 0,
 	    		'sql' => '',
 	    	);
 	    }
 //			$prefix = GetTablePrefix();
 	    $rowcount = 0;
 	    $a_fields = array_keys($a_data[0]);
 	    $fields_sql = '';
 	    foreach ($a_fields AS $field_name) {
 			$fields_sql .= '`'.$field_name.'`,';
 	    }
 		$fields_sql = substr($fields_sql, 0, -1);
 		$temp = '';
 	    foreach ($a_data AS $a_row)
 	    {
 			$values_sql = '';
 			foreach ($a_row as $field_name => $field_value) {
 				$values_sql .= $this->Conn->qstr($field_value).',';
 			}
 			$values_sql = substr($values_sql, 0, -1);
 			$sql = 'INSERT INTO '.$table.' ('.$fields_sql.') VALUES ('.$values_sql.');';
 			$sql = str_replace("\n", "\\n", $sql);
 			$sql = str_replace("\r", "\\r", $sql);
 			$temp .= $sql."\n";
 	    }
 
 		if(strlen(TABLE_PREFIX))
 		{
 			$temp = str_replace("INSERT INTO ".TABLE_PREFIX, "INSERT INTO ", $temp);
 		}
 
     	return Array(
     		'num' => count($a_data),
     		'sql' => $temp,
     	);
 	}
 
 
 
 	function GetTableCreate($table, $crlf="\n")
 	{
 	    $schema_create = 'DROP TABLE IF EXISTS ' . $table . ';' . $crlf;
 		$schema_create .="# --------------------------------------------------------".$crlf;
 	    $this->Conn->Query("SET SQL_QUOTE_SHOW_CREATE = 0");
 	    $tmpres = $this->Conn->Query("SHOW CREATE TABLE $table");
 //			echo "<pre>"; print_r($tmpres); echo "</pre>";
 //			exit;
 	    if(is_array($tmpres) && isset($tmpres[0]))
 	    {
 		    $tmpres = $tmpres[0];
 	        $pos           = strpos($tmpres["Create Table"], ' (');
 	        $pos2          = strpos($tmpres["Create Table"], '(');
 	        if ($pos2 != $pos + 1)
 	        {
 	            $pos = $pos2;
 	            $tmpres["Create Table"] = str_replace(",", ",\n     ", $tmpres["Create Table"]);
 	        }
 
 	        $tmpres["Create Table"]     = substr($tmpres["Create Table"], 0, 13)
 	                       . (($use_backquotes) ? $tmpres["Table"] : $tmpres["Table"])
 	                       . substr($tmpres["Create Table"], $pos);
 	        $tmpres["Create Table"]     = str_replace("\n", $crlf, $tmpres["Create Table"]);
 	        if (preg_match_all('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', $tmpres["Create Table"], $regs)) {
 	            if (!isset($sql_constraints)) {
 	                if (isset($GLOBALS['no_constraints_comments'])) {
 	                    $sql_constraints = '';
 	                } else {
 	                    $sql_constraints = $crlf . '#' . $crlf
 	                                        . '# ' . $GLOBALS['strConstraintsForDumped'] . $crlf
 	                                        . '#' . $crlf;
 	                }
 	            }
 	            if (!isset($GLOBALS['no_constraints_comments'])) {
 	                $sql_constraints .= $crlf .'#' . $crlf .'# ' . $GLOBALS['strConstraintsForTable'] . ' ' . $table . $crlf . '#' . $crlf;
 	            }
 	            $sql_constraints .= 'ALTER TABLE $table $crlf '
 	                             . preg_replace('/(,\r?\n|^)([\s]*)(CONSTRAINT|FOREIGN[\s]*KEY)/', '\1\2ADD \3', substr($regs[0][0], 2))
 	                            . ";\n";
 	            $tmpres["Create Table"]     = preg_replace('((,\r?\n[\s]*(CONSTRAINT|FOREIGN[\s]*KEY)[^\r\n,]+)+)', '', $tmpres["Create Table"]);
 	        }
 	        $schema_create .= $tmpres["Create Table"];
 	    }
 		if(strlen($schema_create))
 		{
 			$schema_create = str_replace("DROP TABLE IF EXISTS ".TABLE_PREFIX,"DROP TABLE ",$schema_create);
 			$schema_create = str_replace("CREATE TABLE ".TABLE_PREFIX,"CREATE TABLE ",$schema_create);
 			while(strlen($schema_create && substr($schema_create,-1)!=")"))
 			{
 				$schema_create = substr($schema_create,0,-1);
 			}
 		}
 		$schema_create .= "\n# --------------------------------------------------------\n";
 	    return $schema_create;
 	}
 
 	/**
 	 * Deletes one backup file
 	 *
 	 * @param kEvent $event
 	 */
 	function OnDeleteBackup(&$event)
 	{
 		@unlink($this->get_backup_file());
 	}
 
 	function get_backup_file()
 	{
 		return $this->Application->ConfigValue('Backup_Path').'/dump'.$this->Application->GetVar('backupdate').'.txt';
 	}
 
 	/**
 	 * Starts restore process
 	 *
 	 * @param kEvent $event
 	 */
 	function OnRestore(&$event)
 	{
 		$file = $this->get_backup_file();
 
 		$restoreProgress = Array (
 			'file_pos' => 0,
 			'file_name' => $file,
 			'file_size' => filesize($file),
 		);
 		$this->Application->RemoveVar('adm.restore_success');
 		$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));
 		$event->redirect = 'tools/restore3';
 	}
 
 	function OnRestoreProgress(&$event)
 	{
 		$done_percent = $this->performRestore();
 
 		if ($done_percent == -3) {
 			$event->redirect = 'tools/restore4';
 			return ;
 		}
 
 		if ($done_percent < 0) {
 			$this->Application->StoreVar('adm.restore_error', 'File read error');			$event->redirect = 'tools/restore4';
 			return ;
 		}
 
 		if ($done_percent == 100) {
 			$this->replaceRestoredFiles();
 			$this->Application->StoreVar('adm.restore_success', 1);
 			$event->redirect = 'tools/restore4';
 			return ;
 		}
 
 		echo $done_percent;
 		$event->status = kEvent::erSTOP;
 
 	}
 
 	function replaceRestoredFiles()
 	{
 		// gather restored table names
 		$tables = $this->Conn->GetCol('SHOW TABLES');
 		$mask_restore_table = '/^restore'.TABLE_PREFIX.'(.*)$/';
 		foreach($tables as $table)
 		{
 			if( preg_match($mask_restore_table,$table,$rets) )
 			{
 				$old_table = substr($table, 7);
 				$this->Conn->Query('DROP TABLE IF EXISTS '.$old_table);
 				$this->Conn->Query('CREATE TABLE '.$old_table.' LIKE '.$table);
 				$this->Conn->Query('INSERT INTO '.$old_table.' SELECT * FROM '.$table);
 				$this->Conn->Query('DROP TABLE '.$table);
 			}
 		}
 
 	}
 
 	function performRestore()
 	{
 		$restoreProgress = unserialize($this->Application->RecallVar('adm.restore_status'));
 		$filename = $restoreProgress['file_name'];
 		$FileOffset = $restoreProgress['file_pos'];
 		$MaxLines = 200;
 		$size = filesize($filename);
 
 		if($FileOffset > $size) {
 			return -2;
 		}
 
 		$fp = fopen($filename,"r");
 		if(!$fp) {
 			return -1;
 		}
 
 
 		if($FileOffset>0)
 		{
 			fseek($fp,$FileOffset);
 		}
 		else
 		{
 			$EndOfSQL = FALSE;
 			$sql = "";
 			while(!feof($fp) && !$EndOfSQL)
 			{
 				$l = fgets($fp);
 				if(substr($l,0,11)=="INSERT INTO")
 				{
 				  $EndOfSQL = TRUE;
 				}
 				else
 				{
 				  $sql .= $l;
 				  $FileOffset = ftell($fp) - strlen($l);
 				}
 			}
 			if(strlen($sql))
 			{
 				$error = $this->runSchemaText($sql);
 				if ($error != '') {
 
 					$this->Application->StoreVar('adm.restore_error', $error);
 					return -3;
 				}
 			}
 			fseek($fp,$FileOffset);
 		}
 
 		$LinesRead = 0;
 		$sql = "";
 		$AllSql = array();
 		while($LinesRead < $MaxLines && !feof($fp))
 		{
 			$sql = fgets($fp);
 			if(strlen($sql))
 			{
 				$AllSql[] = $sql;
 				$LinesRead++;
 			}
 		}
 		if(!feof($fp))
 		{
 			$FileOffset = ftell($fp);
 		}
 		else
 		{
 			$FileOffset = $size;
 		}
 		fclose($fp);
 
 		if(count($AllSql)>0) {
 			$error = $this->runSQLText($AllSql);
 			if ($error != '') {
 
 				$this->Application->StoreVar('adm.restore_error', $error);
 				return -3;
 			}
 
 		}
 		$restoreProgress['file_pos'] = $FileOffset;
 		$this->Application->StoreVar('adm.restore_status', serialize($restoreProgress));
 
 		return round($FileOffset/$size * 100);
 //		$this->Application->StoreVar('adm.restore_error', 'lalalal');
 //		$event->redirect = 'tools/restore4';
 	}
 
 
 	function runSchemaText($sql)
 	{
 		$table_prefix = 'restore'.TABLE_PREFIX;
 //		$table_prefix = TABLE_PREFIX;
 
 		if (strlen($table_prefix) > 0) {
 			$replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
 			foreach ($replacements as $replacement) {
 				$sql = str_replace($replacement, $replacement . $table_prefix, $sql);
 			}
 		}
 
 		$sql = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sql);
 		$sql = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sql);
 
 	    $commands = explode("# --------------------------------------------------------",$sql);
 	    if(count($commands)>0)
 	    {
 //	    	$query_func = getConnectionInterface('query',$dbo_type);
 //	    	$errorno_func = getConnectionInterface('errorno',$dbo_type);
 //	    	$errormsg_func = getConnectionInterface('errormsg',$dbo_type);
 
 	    	for($i = 0; $i < count($commands); $i++)
 	    	{
 	    		$cmd = $commands[$i];
 	    		$cmd = trim($cmd);
 	    		if(strlen($cmd)>0)
 	    		{
 	    			$this->Conn->Query($cmd);
 	    			if($this->Conn->errorCode != 0)
 	    			{
 	    				return $this->Conn->errorMessage." COMMAND:<PRE>$cmd</PRE>";
 	    			}
 	    		}
 	    	}
 	    }
 	}
 
 	function runSQLText($allsql)
 	{
 		$line = 0;
 //		$query_func = getConnectionInterface('query',$dbo_type);
 //		$errorno_func = getConnectionInterface('errorno',$dbo_type);
 //		$errormsg_func = getConnectionInterface('errormsg',$dbo_type);
 
 		while($line<count($allsql))
 		{
 			$sql = $allsql[$line];
 			if(strlen(trim($sql))>0 && substr($sql,0,1)!="#")
 			{
 				$table_prefix = 'restore'.TABLE_PREFIX;
 
 				if (strlen($table_prefix) > 0) {
 					$replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO ');
 					foreach ($replacements as $replacement) {
 						$sql = str_replace($replacement, $replacement . $table_prefix, $sql);
 					}
 				}
 
 				$sql = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sql);
 				$sql = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sql);
 
 				$sql = trim($sql);
 				if(strlen($sql)>0)
 				{
 					$this->Conn->Query($sql);
 
 	    			if($this->Conn->errorCode != 0)
 	    			{
 	    				return $this->Conn->errorMessage." COMMAND:<PRE>$sql</PRE>";
 	    			}
 				}
 			}
 			$line++;
 		}
 	}
 
 
 	/**
 	 * Starts restore process
 	 *
 	 * @param kEvent $event
 	 */
 	function OnSqlQuery(&$event)
 	{
 		$sql = $this->Application->GetVar('sql');
 		if ($sql) {
 			$start = $this->getMoment();
 			$result = $this->Conn->Query($sql);
 			$this->Application->SetVar('sql_time', round($this->getMoment() - $start, 7));
 
 
 			if ($result)
 			{
 				if (is_array($result))
 				{
 					$this->Application->SetVar('sql_has_rows', 1);
 					$this->Application->SetVar('sql_rows', serialize($result));
 				}
 			}
 
 			$check_sql = trim(strtolower($sql));
 			if (
 				(substr($check_sql, 0, 6) == 'insert')
 				|| (substr($check_sql, 0, 6) == 'update')
 				|| (substr($check_sql, 0, 7) == 'replace')
 				|| (substr($check_sql, 0, 6) == 'delete')
 			) {
 				$this->Application->SetVar('sql_has_affected', 1);
 				$this->Application->SetVar('sql_affected', $this->Conn->getAffectedRows());
 			}
 
 		}
 		$this->Application->SetVar('query_status', 1);
 		$event->status = kEvent::erFAIL;
 	}
 
 	function getMoment()
 	{
 		list($usec, $sec) = explode(' ', microtime());
 		return ((float)$usec + (float)$sec);
 	}
 
 	/**
 	 * Occurs after unit config cache was successfully rebuilt
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterCacheRebuild(&$event)
 	{
 
 	}
 
 	/**
 	 * Removes "Community -> Groups" section when it is not allowed
 	 *
 	 * @param kEvent $event
 	 */
 	function OnAfterConfigRead(&$event)
 	{
 		parent::OnAfterConfigRead($event);
 
+		$section_ajustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments');
+
 		if (!$this->Application->ConfigValue('AdvancedUserManagement')) {
-			$section_ajustments = $this->Application->getUnitOption($event->Prefix, 'SectionAdjustments');
 			if (!$section_ajustments) {
 				$section_ajustments = Array ();
 			}
 
 			$section_ajustments['in-portal:user_groups'] = 'remove';
-
-			$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_ajustments);
 		}
+
+		$section_ajustments['in-portal:root'] = Array (
+			'label' => $this->Application->ConfigValue('Site_Name')
+		);
+
+		$this->Application->setUnitOption($event->Prefix, 'SectionAdjustments', $section_ajustments);
 	}
 
 	/**
 	 * Saves menu (tree) frame width
 	 *
 	 * @param kEvent $event
 	 */
 	function OnSaveMenuFrameWidth(&$event)
 	{
 		$event->status = kEvent::erSTOP;
 
 		if (!$this->Application->ConfigValue('ResizableFrames')) {
 			return ;
 		}
 
 		$sql = 'UPDATE ' . $this->Application->getUnitOption('conf', 'TableName') . '
 				SET VariableValue = ' . (int)$this->Application->GetVar('width')  . '
 				WHERE VariableName = "MenuFrameWidth"';
 		$this->Conn->Query($sql);
 
 		if ($this->Conn->getAffectedRows()) {
-			$this->Application->UnitConfigReader->ResetParsedData(false);
+			$this->Application->DeleteUnitCache(false);
 		}
 	}
 
 	/**
 	 * Retrieves data from memory cache
 	 *
 	 * @param kEvent $event
 	 */
 	function OnMemoryCacheGet(&$event)
 	{
 		$event->status = kEvent::erSTOP;
 
 		$ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error
 		$key = $this->Application->GetVar('key');
 
 		if (!$key) {
 			$ret['code'] = 1;
 			$ret['message'] = 'Key name missing';
 		}
 		else {
 			$value = $this->Application->getCache($key);
 
 			$ret['value'] =& $value;
 			$ret['size'] = is_string($value) ? kUtil::formatSize( strlen($value) ) : '?';
 			$ret['type'] = gettype($value);
 
 			if (kUtil::IsSerialized($value)) {
 				$value = unserialize($value);
 			}
 
 			if (is_array($value)) {
 				$ret['value'] = print_r($value, true);
 			}
 
 			if ($ret['value'] === false) {
 				$ret['code'] = 2;
 				$ret['message'] = 'Key "' . $key . '" doesn\'t exist';
 			}
 		}
 
 		$json_helper =& $this->Application->recallObject('JSONHelper');
 		/* @var $json_helper JSONHelper */
 
 		echo $json_helper->encode($ret);
 	}
 
 	/**
 	 * Retrieves data from memory cache
 	 *
 	 * @param kEvent $event
 	 */
 	function OnMemoryCacheSet(&$event)
 	{
 		$event->status = kEvent::erSTOP;
 
 		$ret = Array ('message' => '', 'code' => 0); // 0 - ok, > 0 - error
 		$key = $this->Application->GetVar('key');
 
 		if (!$key) {
 			$ret['code'] = 1;
 			$ret['message'] = 'Key name missing';
 		}
 		else {
 			$value = $this->Application->GetVar('value');
 			$res = $this->Application->setCache($key, $value);
 
 			$ret['result'] = $res ? 'OK' : 'FAILED';
 		}
 
 		$json_helper =& $this->Application->recallObject('JSONHelper');
 		/* @var $json_helper JSONHelper */
 
 		echo $json_helper->encode($ret);
 	}
 }
 
 
 class UnitConfigDecorator {
 
 	var $parentPath = Array ();
 
 	function decorate($var, $level = 0)
 	{
 		$ret = '';
 
 		$deep_level = count($this->parentPath);
 
 		if ($deep_level && ($this->parentPath[0] == 'Fields')) {
 			$expand = $level < 2;
 		}
 		elseif ($deep_level && ($this->parentPath[0] == 'Grids')) {
 			if ($deep_level == 3 && $this->parentPath[2] == 'Icons') {
 				$expand = false;
 			}
 			else {
 				$expand = $level < 4;
 			}
 		}
 		else {
 			$expand = $level == 0;
 		}
 
 		if (is_array($var)) {
 			$ret .= 'Array (';
 			$prepend = $expand ? "\n" . str_repeat("\t", $level + 1) : '';
 
 			foreach ($var as $key => $value) {
 				array_push($this->parentPath, $key);
 				$ret .= $prepend . (is_string($key) ? "'" . $key . "'" : $key) . ' => ' . $this->decorate($value, $level + 1) . ', ';
 				array_pop($this->parentPath);
 			}
 
 			$prepend = $expand ? "\n" . str_repeat("\t", $level) : '';
 			$ret = rtrim($ret, ', ') . $prepend . ')';
 		}
 		else {
 			if (is_null($var)) {
 				$ret = 'NULL';
 			}
 			elseif (is_string($var)) {
 				$ret = "'" . $var . "'";
 			}
 			else {
 				$ret = $var;
 			}
 		}
 
 		return $ret;
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/units/admin/admin_config.php
===================================================================
--- branches/5.2.x/core/units/admin/admin_config.php	(revision 14183)
+++ branches/5.2.x/core/units/admin/admin_config.php	(revision 14184)
@@ -1,99 +1,99 @@
 <?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!');
 
 	$config = Array (
 		'Prefix' => 'adm',
 		'ItemClass' => Array ('class' => 'kDBItem','file'=>'','build_event'=>'OnItemBuild'),
 		'EventHandlerClass' => Array ('class' => 'AdminEventsHandler', 'file' => 'admin_events_handler.php', 'build_event' => 'OnBuild'),
 		'TagProcessorClass' => Array ('class' => 'AdminTagProcessor', 'file' => 'admin_tag_processor.php', 'build_event' => 'OnBuild'),
 
 		'QueryString' => Array (
 			1 => 'event',
 		),
 
 		'TitlePresets' => Array (
 			'tree_root'		=>	Array ('format' => '!la_section_overview!'),
 
 			'tree_reports'	=>	Array ('format' => '!la_section_overview!'),
 
 			'tree_system'	=>	Array ('format' => '!la_section_overview!'),
 
 			'tree_tools'	=>	Array ('format' => '!la_section_overview!'),
 
 			'system_tools'	=>	Array ('format' => '!la_title_SystemTools!'),
 
 			'backup'		=>	Array ('format' => '!la_performing_backup! - !la_Step! <span id="step_number"></span>'),
 			'import'		=>	Array ('format' => '!la_performing_import! - !la_Step! <span id="step_number"></span>'),
 			'restore'		=>	Array ('format' => '!la_performing_restore! - !la_Step! <span id="step_number"></span>'),
 			'server_info'	=>	Array ('format' => '!la_tab_ServerInfo!'),
 			'sql_query'		=>	Array ('format' => '!la_tab_QueryDB!'),
 
 			'no_permissions'	=>	Array ('format' => '!la_title_NoPermissions!'),
 
 			'column_picker'	=>	Array ('format' => '!la_title_ColumnPicker!'),
 			'csv_export'	=>	Array ('format' => '!la_title_CSVExport!'),
 			'csv_import'	=>	Array ('format' => '!la_title_CSVImport!'),
 		),
 
 
 		'PermSection' => Array ('main' => 'in-portal:service'),
 
 		'Sections' => Array (
 			'in-portal:root'	=>	Array (
 				'parent'		=>	null,
 				'icon'			=>	'site',
-				'label'			=>	$this->Application->ConfigValue('Site_Name'),
+				'label'			=>	'SITE_NAME',
 				'url'			=>	Array ('t' => 'index', 'pass' => 'm', 'pass_section' => true, 'no_amp' => 1),
 				'permissions'	=>	Array (),
 				'priority'		=>	0,
 				'container'		=>	true,
 				'type'			=>	stTREE,
 				'icon_module' => 'core',
 			),
 
 			'in-portal:service'	=>	Array (
 				'parent'		=>	'in-portal:tools',
 				'icon'			=>	'service',
 				'label'			=>	'la_tab_Service',
 				'url'			=>	Array ('t' => 'tools/system_tools', 'pass' => 'm'),
 				'permissions'	=>	Array ('view', 'edit'),
 				'priority'		=>	6,
 				'type'			=>	stTREE,
 			),
 		),
 
 		'ListSQLs' => Array (
 			'' => '', // to prevent warning
 		),
 
 		'Fields' => Array (), // we need empty array because kernel doesn't use virtual fields else
 		'VirtualFields' => Array (
 			'ImportFile' => Array (
 				'type' => 'string',
 				'formatter' => 'kUploadFormatter', 'max_size' => MAX_UPLOAD_SIZE, // in Bytes !
 				'error_msgs' => Array (
 					'cant_open_file' => '!la_error_CantOpenFile!',
 					'no_matching_columns' => '!la_error_NoMatchingColumns!',
 				),
 				'file_types' => '*.csv', 'files_description' => '!la_hint_CSVFiles!',
 				'upload_dir' => '/system/import/', // relative to project's home
 				'multiple' => false, 'direct_links' => false,
 				'default' => null,
 			),
 
 			'Content' => Array ('type' => 'string', 'default' => ''),
 		),
 	);
Index: branches/5.2.x/core/units/admin/admin_tag_processor.php
===================================================================
--- branches/5.2.x/core/units/admin/admin_tag_processor.php	(revision 14183)
+++ branches/5.2.x/core/units/admin/admin_tag_processor.php	(revision 14184)
@@ -1,1131 +1,1128 @@
 <?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 AdminTagProcessor extends kDBTagProcessor {
 
 		/**
 		 * Allows to execute js script after the page is fully loaded
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function AfterScript($params)
 		{
 			$after_script = $this->Application->GetVar('after_script');
 			if ($after_script) {
 				return '<script type="text/javascript">'.$after_script.'</script>';
 			}
 			return '';
 		}
 
 		/**
 		 * Returns section title with #section# keyword replaced with current section
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function GetSectionTitle($params)
 		{
 			if (array_key_exists('default', $params)) {
 				return $params['default'];
 			}
 
 			return $this->Application->Phrase( kUtil::replaceModuleSection($params['phrase']) );
 		}
 
 		/**
 		 * Returns section icon with #section# keyword replaced with current section
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function GetSectionIcon($params)
 		{
 			return kUtil::replaceModuleSection($params['icon']);
 		}
 
 		/**
 		 * Returns version of module by name
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function ModuleVersion($params)
 		{
 			return $this->Application->findModule('Name', $params['module'], 'Version');
 		}
 
 		/**
 		 * Used in table form section drawing
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function DrawTree($params)
 		{
 			static $deep_level = 0;
 
 			// when processings, then sort children by priority (key of children array)
 			$ret = '';
 			$section_name = $params['section_name'];
 			$params['name'] = $this->SelectParam($params, 'name,render_as,block');
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			$section_data =& $sections_helper->getSectionData($section_name);
 
 			$params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0;
 			$params['deep_level'] = $deep_level++;
 			$template = $section_data['url']['t'];
 			unset($section_data['url']['t']);
 			$section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']);
 			$ret .= $this->Application->ParseBlock( array_merge($params, $section_data) );
 			if (!isset($section_data['children'])) {
 				return $ret;
 			}
 
 			ksort($section_data['children'], SORT_NUMERIC);
 			foreach ($section_data['children'] as $section_name) {
 				if (!$sections_helper->sectionVisible($section_name)) {
 					continue;
 				}
 
 				$params['section_name'] = $section_name;
 				$ret .= $this->DrawTree($params);
 				$deep_level--;
 			}
 
 
 			return $ret;
 		}
 
 
 		function SectionInfo($params)
 		{
 			$section = $params['section'];
 			if ($section == '#session#') {
 				$section = $this->Application->RecallVar('section');
 			}
 
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			$section_data =& $sections_helper->getSectionData($section);
 			if (!$section_data) {
 				throw new Exception('Use of undefined section "<strong>' . $section . '</strong>" in "<strong>' . __METHOD__ . '</strong>"');
 				return '';
 			}
 
 			if (array_key_exists('parent', $params) && $params['parent']) {
 				do {
 					$section = $section_data['parent'];
 					$section_data =& $sections_helper->getSectionData($section);
 				} while (array_key_exists('use_parent_header', $section_data) && $section_data['use_parent_header']);
 			}
 
 			$info = $params['info'];
 			switch ($info) {
 				case 'module_path':
 					if (isset($params['module']) && $params['module']) {
 						$module = $params['module'];
 					}
 					elseif (isset($section_data['icon_module'])) {
 						$module = $section_data['icon_module'];
 					}
 					else {
 						$module = '#session#';
 					}
 					$res = $this->ModulePath(array('module' => $module));
 					break;
 
 				case 'perm_section':
 					$res = $sections_helper->getPermSection($section);
 					break;
 
 				case 'label':
 					if ($section && ($section != 'in-portal:root')) {
 						// don't translate label for top section, because it's already translated
 						$no_editing = array_key_exists('no_editing', $params) ? $params['no_editing'] : false;
 
 						$res = $this->Application->Phrase($section_data['label'], !$no_editing);
 					}
 					else {
 						$res = '';
 					}
 					break;
 
 				default:
 					$res = $section_data[$info];
 					break;
 			}
 
 			if (array_key_exists('as_label', $params) && $params['as_label']) {
 				$res = $this->Application->Phrase($res);
 			}
 
 			return $res;
 		}
 
 
 		function PrintSection($params)
 		{
 			$section_name = $params['section_name'];
 			if ($section_name == '#session#') {
 				$section_name = $this->Application->RecallVar('section');
 			}
 
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			if (isset($params['use_first_child']) && $params['use_first_child']) {
 				$section_name = $sections_helper->getFirstChild($section_name, true);
 			}
 
 			$section_data =& $sections_helper->getSectionData($section_name);
 
 			$params['name'] = $this->SelectParam($params, 'name,render_as,block');
 			$params['section_name'] = $section_name;
 
 			$template = $section_data['url']['t'];
 			unset($section_data['url']['t']);
 
 			$section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']);
 			$ret = $this->Application->ParseBlock( array_merge($params, $section_data) );
 
 			return $ret;
 		}
 
 		/**
 		 * Used in XML drawing for tree
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function PrintSections($params)
 		{
 			// when processings, then sort children by priority (key of children array)
 			$ret = '';
 			$section_name = $params['section_name'];
 			if ($section_name == '#session#') {
 				$section_name = $this->Application->RecallVar('section');
 			}
 
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			$section_data =& $sections_helper->getSectionData($section_name);
 
 			$params['name'] = $this->SelectParam($params, 'name,render_as,block');
 			if (!isset($section_data['children'])) {
 				return '';
 			}
 
 			ksort($section_data['children'], SORT_NUMERIC);
 			foreach ($section_data['children'] as $section_name) {
 				$params['section_name'] = $section_name;
 				$section_data =& $sections_helper->getSectionData($section_name);
 
 				if (!$sections_helper->sectionVisible($section_name)) {
 					continue;
 				}
 				else {
 					$show_mode = $section_data['show_mode'];
 					$section_data['debug_only'] = ($show_mode == smDEBUG) || ($show_mode == smSUPER_ADMIN) ? 1 : 0;
 				}
 
 				if (isset($section_data['tabs_only']) && $section_data['tabs_only']) {
 					$perm_status = false;
 					$folder_label = $section_data['label'];
 					ksort($section_data['children'], SORT_NUMERIC);
 					foreach ($section_data['children'] as $priority => $section_name) {
 						// if only tabs in this section & none of them have permission, then skip section too
 
 						$section_name = $sections_helper->getPermSection($section_name);
 						$perm_status = $this->Application->CheckPermission($section_name.'.view', 1);
 						if ($perm_status) {
 							break;
 						}
 					}
 					if (!$perm_status) {
 						// no permission for all tabs -> don't display tree node either
 						continue;
 					}
 
 					$params['section_name'] = $section_name;
 					$section_data =& $sections_helper->getSectionData($section_name);
 					$section_data['label'] = $folder_label; // use folder label in tree
 					$section_data['is_tab'] = 1;
 				}
 				else  {
 					$section_name = $sections_helper->getPermSection($section_name);
 					if (!$this->Application->CheckPermission($section_name.'.view', 1)) continue;
 				}
 
 				$params['children_count'] = isset($section_data['children']) ? count($section_data['children']) : 0;
 
 				// remove template, so it doesn't appear as additional parameter in url
 				$template = $section_data['url']['t'];
 				unset($section_data['url']['t']);
 
 				$section_data['section_url'] = $this->Application->HREF($template, '', $section_data['url']);
 
 				$late_load = getArrayValue($section_data, 'late_load');
 				if ($late_load) {
 					$t = $late_load['t'];
 					unset($late_load['t']);
 					$section_data['late_load'] = $this->Application->HREF($t, '', $late_load);
 					$params['children_count'] = 99;
 				}
 				else {
 					$section_data['late_load'] = '';
 				}
 
 				// restore template
 				$section_data['url']['t'] = $template;
 
 				$ret .= $this->Application->ParseBlock( array_merge($params, $section_data) );
 				$params['section_name'] = $section_name;
 			}
 
 			return preg_replace("/\r\n|\n/", '', $ret);
 		}
 
 		function ListSectionPermissions($params)
 		{
 			$section_name = isset($params['section_name']) ? $params['section_name'] : $this->Application->GetVar('section_name');
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			$section_data =& $sections_helper->getSectionData($section_name);
 
 			$block_params = array_merge($section_data, Array('name' => $params['render_as'], 'section_name' => $section_name));
 
 			$ret = '';
 			foreach ($section_data['permissions'] as $perm_name) {
 				if (preg_match('/^advanced:(.*)/', $perm_name) != $params['type']) continue;
 				$block_params['perm_name'] = $perm_name;
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 			return $ret;
 		}
 
 		function ModuleInclude($params)
 		{
 			foreach ($params as $param_name => $param_value) {
 				$params[$param_name] = kUtil::replaceModuleSection($param_value);
 			}
 
 			$m =& $this->Application->recallObject('m_TagProcessor');
 			return $m->ModuleInclude($params);
 		}
 
 		function TodayDate($params)
 		{
 			return date($params['format']);
 		}
 
 		function TreeEditWarrning($params)
 		{
 			$ret = $this->Application->Phrase($params['label']);
 			$ret = str_replace(Array('&lt;', '&gt;', 'br/', 'br /', "\n", "\r"), Array('<', '>', 'br', 'br', '', ''), $ret);
 			if (getArrayValue($params, 'escape')) {
 				$ret = addslashes($ret);
 			}
 			$ret = str_replace('<br>', '\n', $ret);
 			return $ret;
 		}
 
 		/**
 		 * Draws section tabs using block name passed
 		 *
 		 * @param Array $params
 		 */
 		function ListTabs($params)
 		{
 			$sections_helper =& $this->Application->recallObject('SectionsHelper');
 			$section_data =& $sections_helper->getSectionData($params['section_name']);
 
 			$ret = '';
 			$block_params = Array('name' => $params['render_as']);
 			ksort($section_data['children'], SORT_NUMERIC);
 			foreach ($section_data['children'] as $priority => $section_name) {
 				if (!$this->Application->CheckPermission($section_name.'.view', 1)) continue;
 
 				$tab_data =& $sections_helper->getSectionData($section_name);
 				$block_params['t'] = $tab_data['url']['t'];
 				$block_params['title'] = $tab_data['label'];
 				$block_params['main_prefix'] = $section_data['SectionPrefix'];
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 
 
 			return $ret;
 		}
 
 		/**
 		 * Returns list of module item tabs that have view permission in current category
 		 *
 		 * @param Array $params
 		 */
 		function ListCatalogTabs($params)
 		{
 			$ret = '';
 			$special = isset($params['special']) ? $params['special'] : '';
 			$replace_main = isset($params['replace_m']) && $params['replace_m'];
 			$skip_prefixes = isset($params['skip_prefixes']) ? explode(',', $params['skip_prefixes']) : Array();
 
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				$prefix = $module_info['Var'];
 
 				if ($prefix == 'm' && $replace_main) {
 					$prefix = 'c';
 				}
 
 				if (in_array($prefix, $skip_prefixes) || !$this->Application->prefixRegistred($prefix) || !$this->Application->getUnitOption($prefix, 'CatalogItem')) {
 					continue;
 				}
 
 				$icon = $this->Application->getUnitOption($prefix, 'CatalogTabIcon');
 				if (strpos($icon, ':') !== false) {
 					list ($icon_module, $icon) = explode(':', $icon, 2);
 				}
 				else {
 					$icon_module = 'core';
 				}
 
 				$label = $this->Application->getUnitOption($prefix, $params['title_property']);
 				$block_params['title'] = $label;
 				$block_params['prefix'] = $prefix;
 				$block_params['icon_module'] = $icon_module;
 				$block_params['icon'] = $icon;
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 			return $ret;
 		}
 
 		/**
 		 * Renders inividual catalog tab based on prefix and title_property given
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function CatalogTab($params)
 		{
 			$icon = $this->Application->getUnitOption($params['prefix'], 'CatalogTabIcon');
 			if (strpos($icon, ':') !== false) {
 				list ($icon_module, $icon) = explode(':', $icon, 2);
 			}
 			else {
 				$icon_module = 'core';
 			}
 
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 			$block_params['icon_module'] = $icon_module;
 			$block_params['icon'] = $icon;
 			$block_params['title'] = $this->Application->getUnitOption($params['prefix'], $params['title_property']);
 
 			return $this->Application->ParseBlock($block_params);
 		}
 
 		/**
 		 * Allows to construct link for opening any type of catalog item selector
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function SelectorLink($params)
 		{
 			$mode = 'catalog';
 			if (isset($params['mode'])) { // {catalog, advanced_view}
 				$mode = $params['mode'];
 				unset($params['mode']);
 			}
 
 			$params['t'] = 'catalog/item_selector/item_selector_'.$mode;
 			$params['m_cat_id'] = $this->Application->getBaseCategory();
 
 			$default_params = Array('no_amp' => 1, 'pass' => 'all,'.$params['prefix']);
 			unset($params['prefix']);
 
 			$pass_through = Array();
 			if (isset($params['tabs_dependant'])) { // {yes, no}
 				$pass_through['td'] = $params['tabs_dependant'];
 				unset($params['tabs_dependant']);
 			}
 
 			if (isset($params['selection_mode'])) { // {single, multi}
 				$pass_through['tm'] = $params['selection_mode'];
 				unset($params['selection_mode']);
 			}
 
 			if (isset($params['tab_prefixes'])) { // {all, none, <comma separated prefix list>}
 				$pass_through['tp'] = $params['tab_prefixes'];
 				unset($params['tab_prefixes']);
 			}
 
 			if ($pass_through) {
 				// add pass_through to selector url if any
 				$params['pass_through'] = implode(',', array_keys($pass_through));
 				$params = array_merge($params, $pass_through);
 			}
 
 			// user can override default parameters (except pass_through of course)
 			$params = array_merge($default_params, $params);
 
 			$main_processor =& $this->Application->recallObject('m_TagProcessor');
 	    	return $main_processor->T($params);
 		}
 
 		function TimeFrame($params)
 		{
 			$w = adodb_date('w');
 			$m = adodb_date('m');
 			$y = adodb_date('Y');
 			//FirstDayOfWeek is 0 for Sunday and 1 for Monday
 			$fdow = $this->Application->ConfigValue('FirstDayOfWeek');
 			if ($fdow && $w == 0) $w = 7;
 			$today_start = adodb_mktime(0,0,0,adodb_date('m'),adodb_date('d'),$y);
 			$first_day_of_this_week = $today_start - ($w - $fdow)*86400;
 			$first_day_of_this_month = adodb_mktime(0,0,0,$m,1,$y);
 			$this_quater = ceil($m/3);
 			$this_quater_start = adodb_mktime(0,0,0,$this_quater*3-2,1,$y);
 
 			switch ($params['type']) {
 				case 'last_week_start':
 					$timestamp = $first_day_of_this_week - 86400*7;
 					break;
 				case 'last_week_end':
 					$timestamp = $first_day_of_this_week - 1;
 					break;
 
 				case 'last_month_start':
 					$timestamp = $m == 1 ? adodb_mktime(0,0,0,12,1,$y-1) : adodb_mktime(0,0,0,$m-1,1,$y);
 					break;
 				case 'last_month_end':
 					$timestamp = $first_day_of_this_month = adodb_mktime(0,0,0,$m,1,$y) - 1;
 					break;
 
 				case 'last_quater_start':
 					$timestamp = $this_quater == 1 ? adodb_mktime(0,0,0,10,1,$y-1) : adodb_mktime(0,0,0,($this_quater-1)*3-2,1,$y);
 					break;
 				case 'last_quater_end':
 					$timestamp = $this_quater_start - 1;
 					break;
 
 				case 'last_6_months_start':
 					$timestamp = $m <= 6 ? adodb_mktime(0,0,0,$m+6,1,$y-1) : adodb_mktime(0,0,0,$m-6,1,$y);
 					break;
 
 				case 'last_year_start':
 					$timestamp = adodb_mktime(0,0,0,1,1,$y-1);
 					break;
 				case 'last_year_end':
 					$timestamp = adodb_mktime(23,59,59,12,31,$y-1);
 					break;
 			}
 
 
 			if (isset($params['format'])) {
 				$format = $params['format'];
 				if(preg_match("/_regional_(.*)/", $format, $regs))
 				{
 					$lang =& $this->Application->recallObject('lang.current');
 					$format = $lang->GetDBField($regs[1]);
 				}
 				return adodb_date($format, $timestamp);
 			}
 
 			return $timestamp;
 
 		}
 
 		/**
 		 * Redirect to cache rebuild template, when required by installator
 		 *
 		 * @param Array $params
 		 */
 		function CheckPermCache($params)
 		{
 			// we have separate session between install wizard and admin console, so store in cache
 			$global_mark = $this->Application->getDBCache('ForcePermCacheUpdate');
 			$local_mark = $this->Application->RecallVar('PermCache_UpdateRequired');
 
 			if ($global_mark || $local_mark) {
 				$this->Application->RemoveVar('PermCache_UpdateRequired');
 
 				if ($this->Application->ConfigValue('QuickCategoryPermissionRebuild')) {
 					$updater =& $this->Application->makeClass('kPermCacheUpdater');
 					/* @var $updater kPermCacheUpdater */
 
 					$updater->OneStepRun();
 				}
 				else {
 					// update with progress bar
 					return true;
 				}
 			}
 
 			return false;
 		}
 
 		/**
 		 * Checks if current protocol is SSL
 		 *
 		 * @param Array $params
 		 * @return int
 		 */
 		function IsSSL($params)
 		{
 			return (PROTOCOL == 'https://')? 1 : 0;
 		}
 
 		function PrintColumns($params)
 		{
 			$picker_helper =& $this->Application->RecallObject('ColumnPickerHelper');
 			$picker_helper->SetGridName($this->Application->GetLinkedVar('grid_name'));
 			/* @var $picker_helper kColumnPickerHelper */
 
 			$main_prefix = $this->Application->RecallVar('main_prefix');
 			$cols = $picker_helper->LoadColumns($main_prefix);
 
 			$this->Application->Phrases->AddCachedPhrase('__FREEZER__', '-------------');
 
 			$o = '';
 			if (isset($params['hidden']) && $params['hidden']) {
 				foreach ($cols['hidden_fields'] as $col) {
 					$title = $this->Application->Phrase($cols['titles'][$col]);
 					$o .= "<option value='$col'>".$title;
 				}
 			}
 			else {
 				foreach ($cols['order'] as $col) {
 					if (in_array($col, $cols['hidden_fields'])) continue;
 					$title = $this->Application->Phrase($cols['titles'][$col]);
 					$o .= "<option value='$col'>".$title;
 				}
 			}
 			return $o;
 		}
 
 		/**
 		 * Allows to set popup size (key - current template name)
 		 *
 		 * @param Array $params
 		 */
 		function SetPopupSize($params)
 		{
 			$width = $params['width'];
 			$height = $params['height'];
 
 			if ($this->Application->GetVar('ajax') == 'yes') {
 				// during AJAX request just output size
 				die($width.'x'.$height);
 			}
 
 			if (!$this->UsePopups($params)) {
 				return ;
 			}
 
 			$t = $this->Application->GetVar('t');
 			$sql = 'SELECT *
 					FROM '.TABLE_PREFIX.'PopupSizes
 					WHERE TemplateName = '.$this->Conn->qstr($t);
 			$popup_info = $this->Conn->GetRow($sql);
 			if (!$popup_info) {
 				// create new popup size record
 				$fields_hash = 	Array (
 										'TemplateName'	=>	$t,
 										'PopupWidth'	=>	$width,
 										'PopupHeight'	=>	$height,
 								);
 				$this->Conn->doInsert($fields_hash, TABLE_PREFIX.'PopupSizes');
 			}
 			elseif ($popup_info['PopupWidth'] != $width || $popup_info['PopupHeight'] != $height) {
 				// popup found and size in tag differs from one in db -> update in db
 				$fields_hash = 	Array (
 										'PopupWidth'	=>	$width,
 										'PopupHeight'	=>	$height,
 								);
 				$this->Conn->doUpdate($fields_hash, TABLE_PREFIX.'PopupSizes', 'PopupId = '.$popup_info['PopupId']);
 			}
 		}
 
 		/**
 		 * Returns popup size (by template), if not cached, then parse template to get value
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function GetPopupSize($params)
 		{
 			$t = $this->Application->GetVar('template_name');
 			$sql = 'SELECT *
 					FROM '.TABLE_PREFIX.'PopupSizes
 					WHERE TemplateName = '.$this->Conn->qstr($t);
 			$popup_info = $this->Conn->GetRow($sql);
 			if (!$popup_info) {
 				$this->Application->InitParser();
 				$this->Application->ParseBlock(array('name' => $t)); // dies when SetPopupSize tag found & in ajax requrest
 				return '750x400'; // tag SetPopupSize not found in template -> use default size
 			}
 			return $popup_info['PopupWidth'].'x'.$popup_info['PopupHeight'];
 		}
 
 		/**
 		 * Allows to check if popups are generally enabled OR to check for "popup" or "modal" mode is enabled
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function UsePopups($params)
 		{
 			if ($this->Application->GetVar('_force_popup')) {
 				return true;
 			}
 
 			$use_popups = (int)$this->Application->ConfigValue('UsePopups');
 
 			if (array_key_exists('mode', $params)) {
 				$mode_mapping = Array ('popup' => 1, 'modal' => 2);
 				return $use_popups == $mode_mapping[ $params['mode'] ];
 			}
 
 			return $use_popups;
 		}
 
 		function UseToolbarLabels($params)
 		{
 			return (int)$this->Application->ConfigValue('UseToolbarLabels');
 		}
 
 		/**
 		 * Checks if debug mode enabled (optionally) and specified constant is on
 		 *
 		 * @param Array $params
 		 * @return bool
 		 * @todo Could be a duplicate of kMainTagProcessor::ConstOn
 		 */
 		function ConstOn($params)
 		{
 			$constant_name = $this->SelectParam($params, 'name,const');
 			$debug_mode = isset($params['debug_mode']) && $params['debug_mode'] ? $this->Application->isDebugMode() : true;
 
 			return $debug_mode && kUtil::constOn($constant_name);
 		}
 
 		/**
 		 * Builds link to last template in main frame of admin
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function MainFrameLink($params)
 		{
 			$persistent = isset($params['persistent']) && $params['persistent'];
 			if ($persistent && $this->Application->ConfigValue('RememberLastAdminTemplate')) {
 				// check last_template in persistent session
 				$last_template = $this->Application->RecallPersistentVar('last_template_popup');
 			}
 			else {
 				// check last_template in session
 				$last_template = $this->Application->RecallVar('last_template_popup'); // because of m_opener=s there
 			}
 
 			if (!$last_template) {
 				$params['persistent'] = 1;
 				return $persistent ? false : $this->MainFrameLink($params);
 			}
 
 			list($index_file, $env) = explode('|', $last_template);
 			$vars = $this->Application->HttpQuery->processQueryString($env, 'pass');
 			$recursion_templates = Array ('login', 'index', 'no_permission');
 
 			if (isset($vars['admin']) && $vars['admin'] == 1) {
 				// index template doesn't begin recursion on front-end (in admin frame)
 				$vars['m_theme'] = '';
 
 				if (isset($params['m_opener']) && $params['m_opener'] == 'r') {
 					// front-end link for highlighting purposes
 					$vars['t'] = 'index';
 					$vars['m_cat_id'] = $this->Application->getBaseCategory();
 				}
 
 				unset($recursion_templates[ array_search('index', $recursion_templates)]);
 			}
 
 			if (in_array($vars['t'], $recursion_templates)) {
 				// prevents redirect recursion OR old in-portal pages
 				$params['persistent'] = 1;
 				return $persistent ? false : $this->MainFrameLink($params);
 			}
 
 			$vars = array_merge($vars, $params);
 			$t = $vars['t'];
 			unset($vars['t'], $vars['persistent']);
 
 			// substitute language in link to current (link will work, even when language will be changed)
 			$vars['m_lang'] = $this->Application->GetVar('m_lang');
 
 			return $this->Application->HREF($t, '', $vars, $index_file);
 		}
 
 		/**
 		 * Returns menu frame width or 200 in case, when invalid width specified in config
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function MenuFrameWidth($params)
 		{
 			$width = (int)$this->Application->ConfigValue('MenuFrameWidth');
 
 			return $width > 0 ? $width : 200;
 		}
 
 		function AdminSkin($params)
 		{
 			$skin_helper =& $this->Application->recallObject('SkinHelper');
 			/* @var $skin_helper SkinHelper */
 
 			return $skin_helper->AdminSkinTag($params);
 		}
 
 		function PrintCompileErrors($params)
 		{
 			$block_params = $this->prepareTagParams($params);
 			$block_params['name'] = $params['render_as'];
 
 			$errors = $this->Application->RecallVar('compile_errors');
 			if (!$errors) {
 				return ;
 			}
 
 			$ret = '';
 			$errors = unserialize($errors);
 
 			foreach ($errors as $an_error) {
 				$block_params['file'] = str_replace(FULL_PATH, '', $an_error['file']);
 				$block_params['line'] = $an_error['line'];
 				$block_params['message'] = $an_error['msg'];
 
 				$ret .= $this->Application->ParseBlock($block_params);
 			}
 
 			$this->Application->RemoveVar('compile_errors');
 
 			return $ret;
 		}
 
 		function CompileErrorCount($params)
 		{
 			$errors = $this->Application->RecallVar('compile_errors');
 			if (!$errors) {
 				return 0;
 			}
 
 			return count( unserialize($errors) );
 		}
 
 		function ExportData($params)
 		{
 			$export_helper =& $this->Application->recallObject('CSVHelper');
 			/* @var $export_helper kCSVHelper */
 			$result = $export_helper->ExportData( $this->SelectParam($params, 'var,name,field') );
 			return ($result === false) ? '' : $result;
 		}
 
 		function ImportData($params)
 		{
 			$import_helper =& $this->Application->recallObject('CSVHelper');
 			/* @var $import_helper kCSVHelper */
 			$result = $import_helper->ImportData( $this->SelectParam($params, 'var,name,field') );
 			return ($result === false) ? '' : $result;
 		}
 
 		function PrintCSVNotImportedLines($params)
 		{
 			$import_helper =& $this->Application->recallObject('CSVHelper');
 			/* @var $import_helper kCSVHelper */
 			return  $import_helper->GetNotImportedLines();
 		}
 
 		/**
 		 * Returns input field name to
 		 * be placed on form (for correct
 		 * event processing)
 		 *
 		 * @param Array $params
 		 * @return string
 		 * @access public
 		 */
 		function InputName($params)
 		{
 			list($id, $field) = $this->prepareInputName($params);
 
 			$ret = $this->getPrefixSpecial().'[0]['.$field.']'; // 0 always, as has no idfield
 			if( getArrayValue($params, 'as_preg') ) $ret = preg_quote($ret, '/');
 			return $ret;
 		}
 
 		/**
 		 * Returns list of all backup file dates formatted
 		 * in passed block
 		 *
 		 * @param Array $params
 		 * @return string
 		 * @access public
 		 */
 		function PrintBackupDates($params)
 		{
 	 		$datearray = $this->getDirList($this->Application->ConfigValue('Backup_Path'));
 	 		$ret = '';
 	        foreach($datearray as $key => $value)
 	        {
 	        	$params['backuptimestamp'] = $value['filedate'];
 	        	$params['backuptime'] = date('F j, Y, g:i a', $value['filedate']);
 	        	$params['backupsize'] = round($value['filesize']/1024/1024, 2); // MBytes
 	        	$ret .= $this->Application->ParseBlock($params);
 	        }
 	        return $ret;
 		}
 
 		function getDirList ($dirName)
 		{
 		    $file_helper =& $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$file_helper->CheckFolder($dirName);
 
 			$fileinfo = array();
 		    $d = dir($dirName);
 
 		    while($entry = $d->read())
 		    {
 		        if ($entry != "." && $entry != "..")
 		        {
 		            if (!is_dir($dirName."/".$entry) && strpos($entry, 'dump') !== false)
 		            {
 		                $fileinfo[]= Array('filedate' => $this->chopchop($entry),
 		                					'filesize' => filesize($dirName. '/'. $entry)
 		                					);
 		            }
 		        }
 		    }
 		    $d->close();
 		    rsort($fileinfo);
 
 		    return $fileinfo;
 
 		}
 
 		function chopchop ($filename)
 		{
 			 $p = pathinfo($filename);
 			 $ext = '.'.$p['extension'];
 		     $filename;
 		     $filename = str_replace('dump', '',$filename);
 		     $filename = str_replace($ext, '', $filename);
 
 		     return $filename;
 		}
 
 		/**
 		 * Returns phpinfo() output
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function PrintPHPinfo($params)
 		{
 			ob_start();
 			phpinfo();
 
 			return ob_get_clean();
 		}
 
 		function PrintSqlCols($params)
 		{
 			$a_data = unserialize($this->Application->GetVar('sql_rows'));
 			$ret = '';
 			$block = $params['render_as'];
 			foreach ($a_data AS $a_row)
 			{
 				foreach ($a_row AS $col => $value)
 				{
 					$ret .= $this->Application->ParseBlock(Array('name'=>$block, 'value'=>$col));
 				}
 				break;
 			}
 			return $ret;
 		}
 
 		function PrintSqlRows($params)
 		{
 			$a_data = unserialize($this->Application->GetVar('sql_rows'));
 			$ret = '';
 			$block = $params['render_as'];
 			foreach ($a_data AS $a_row)
 			{
 				$cells = '';
 				foreach ($a_row AS $col => $value)
 				{
 					$cells .= '<td>'.$value.'</td>';
 				}
 				$ret .= $this->Application->ParseBlock(Array('name'=>$block, 'cells'=>$cells));
 			}
 			return $ret;
 		}
 
 		/**
 		 * Prints available and enabled import sources using given block
 		 *
 		 * @param Array $params
 		 * @return string
 		 */
 		function PrintImportSources($params)
  		{
  			$sql = 'SELECT *
  					FROM ' . TABLE_PREFIX . 'ImportScripts
  					WHERE (Status = ' . STATUS_ACTIVE . ') AND (Type = "CSV")';
  			$import_sources = $this->Conn->Query($sql);
 
  			$block_params = $this->prepareTagParams($params);
  			$block_params['name'] = $params['render_as'];
 
  			$ret = '';
  			foreach ($import_sources as $import_source) {
  				$block_params['script_id'] = $import_source['ImportId'];
  				$block_params['script_module'] = mb_strtolower($import_source['Module']);
  				$block_params['script_name'] = $import_source['Name'];
  				$block_params['script_prefix'] = $import_source['Prefix'];
  				$block_params['module_path'] = $this->Application->findModule('Name', $import_source['Module'], 'Path');
 
  				$ret .= $this->Application->ParseBlock($block_params);
  			}
 
  			return $ret;
  		}
 
  		/**
  		 * Checks, that new window should be opened in "incs/close_popup" template instead of refreshing parent window
  		 *
  		 * @param Array $params
  		 * @return bool
  		 */
  		function OpenNewWindow($params)
  		{
 			if (!$this->UsePopups($params)) {
 				return false;
 			}
 
  			$diff = array_key_exists('diff', $params) ? $params['diff'] : 0;
  			$wid = $this->Application->GetVar('m_wid');
 
 			$stack_name = rtrim('opener_stack_' . $wid, '_');
 			$opener_stack = $this->Application->RecallVar($stack_name);
 			$opener_stack = $opener_stack ? unserialize($opener_stack) : Array ();
 
 			return count($opener_stack) >= 2 - $diff;
  		}
 
 		/**
 		 * Allows to dynamically change current language in template
 		 *
 		 * @param Array $params
 		 */
 		function SetLanguage($params)
 		{
 			$this->Application->SetVar('m_lang', $params['language_id']);
-			$this->Application->Phrases->Init('');
-
-			$this->Application->Phrases->LanguageId = $params['language_id'];
-			$this->Application->Phrases->LoadPhrases( $this->Application->Caches['PhraseList'] );
+			$this->Application->Phrases->Init('phrases', '', $params['language_id']);
 		}
 
 		/**
 		 * Performs HTTP Authentification for administrative console
 		 *
 		 * @param Array $params
 		 */
 		function HTTPAuth($params)
 		{
 			if (!$this->Application->ConfigValue('UseHTTPAuth')) {
 				// http authentification not required
 				return true;
 			}
 
 			$super_admin_ips = defined('SA_IP') ? SA_IP : false;
 			$auth_bypass_ips = $this->Application->ConfigValue('HTTPAuthBypassIPs');
 
 			if (($auth_bypass_ips && kUtil::ipMatch($auth_bypass_ips)) || ($super_admin_ips && kUtil::ipMatch($super_admin_ips))) {
 				// user ip is in ip bypass list
 				return true;
 			}
 
 			if (!array_key_exists('PHP_AUTH_USER', $_SERVER)) {
 				// ask user to authentificate, when not authentificated before
 				return $this->_httpAuthentificate();
 			}
 			else {
 				// validate user credentials (browsers remembers user/password
 				// and sends them each time page is visited, so no need to save
 				// authentification result in session)
 				if ($this->Application->ConfigValue('HTTPAuthUsername') != $_SERVER['PHP_AUTH_USER']) {
 					// incorrect username
 					return $this->_httpAuthentificate();
 				}
 
 				$password_formatter =& $this->Application->recallObject('kPasswordFormatter');
 				/* @var $password_formatter kPasswordFormatter */
 
 				$password = $password_formatter->EncryptPassword($_SERVER['PHP_AUTH_PW'], 'b38');
 
 				if ($this->Application->ConfigValue('HTTPAuthPassword') != $password) {
 					// incorrect password
 					return $this->_httpAuthentificate();
 				}
 			}
 
 			return true;
 		}
 
 		/**
 		 * Ask user to authentificate
 		 *
 		 * @return false
 		 */
 		function _httpAuthentificate()
 		{
 			$realm = strip_tags( $this->Application->ConfigValue('Site_Name') );
 			header('WWW-Authenticate: Basic realm="' . $realm . '"');
 			header('HTTP/1.0 401 Unauthorized');
 
 			return false;
 		}
 
 		/**
 		 * Checks, that we are using memory cache
 		 *
 		 * @param Array $params
 		 * @return bool
 		 */
 		function MemoryCacheEnabled($params)
 		{
 			return $this->Application->isCachingType(CACHING_TYPE_MEMORY);
 		}
 	}
\ No newline at end of file