Index: branches/5.0.x/core/kernel/utility/unit_config_reader.php
===================================================================
--- branches/5.0.x/core/kernel/utility/unit_config_reader.php	(revision 13241)
+++ branches/5.0.x/core/kernel/utility/unit_config_reader.php	(revision 13242)
@@ -1,992 +1,992 @@
 <?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 {
 
 	/**
 	 * 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 = '';
 
 	/**
 	 * Folders to skip during unit config search
 	 *
 	 * @var Array
 	 */
 	var $_skipFolders = Array ('CVS', '.svn', 'admin_templates', 'libchart');
 
 	/**
 	 * Scan kernel and user classes
 	 * for available configs
 	 *
 	 * @access protected
 	 */
 	function Init($prefix,$special)
 	{
 		parent::Init($prefix,$special);
 
 		$this->_directorySeparator = preg_quote( DIRECTORY_SEPARATOR );
 		$this->_skipFolders[] = trim(WRITEBALE_BASE, '/'); // system folder (where web server can write)
 
 		$editor_path = explode('/', trim(EDITOR_PATH, '/'));
 		$this->_skipFolders[] = array_pop($editor_path); // last of cmseditor folders
 	}
 
 	function CacheParsedData()
 	{
 		$event_manager =& $this->Application->recallObject('EventManager');
 		$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 
 		$config_vars = Array (
 			'SessionTimeout',
 			'SessionCookieName',
 			'SessionBrowserSignatureCheck',
 			'SessionIPAddressCheck',
 			'CookieSessions',
 			'UseCronForRegularEvent',
 			'User_GuestGroup',
 			'User_LoggedInGroup',
 			'SessionTimeout',
 			'UseModRewrite',
 			'UseOutputCompression',
 			'OutputCompressionLevel',
 			'KeepSessionOnBrowserClose',
 			'Config_Server_Time',
 			'Config_Site_Time',
 			'UseChangeLog',
 			'UseVisitorTracking',
 		);
 
 		foreach ($config_vars as $var) {
 			$this->Application->ConfigValue($var);
 		}
 
 		$cache = Array(
 		'Factory.Files' => $this->Application->Factory->Files,
 		'Factory.realClasses' => $this->Application->Factory->realClasses,
 		'Factory.Dependencies' => $this->Application->Factory->Dependencies,
 		'ConfigReader.prefixFiles' => $this->prefixFiles,
 		'EventManager.buildEvents' => $event_manager->buildEvents,
 		'EventManager.beforeRegularEvents' => $event_manager->beforeRegularEvents,
 		'EventManager.afterRegularEvents' => $event_manager->afterRegularEvents,
 		'EventManager.beforeHooks' => $event_manager->beforeHooks,
 		'EventManager.afterHooks' => $event_manager->afterHooks,
 		'TagsAggregator.data' => $aggregator->_Array,
 
 		// the following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
 		'Application.Caches.ConfigVariables' => $this->Application->Caches['ConfigVariables'],
 		'Application.ConfigCacheIds' => $this->Application->ConfigCacheIds,
 		'Application.ConfigHash' => $this->Application->ConfigHash,
 		'Application.ReplacementTemplates' => $this->Application->ReplacementTemplates,
 		'Application.RewriteListeners' => $this->Application->RewriteListeners,
 		'Application.ModuleInfo' => $this->Application->ModuleInfo,
 		);
 
 		$conn =& $this->Application->GetADODBConnection();
 		if (isset($this->Application->Memcached)) {
 			$this->Application->Memcached->set('master:configs_parsed', serialize($cache));
 			$this->Application->Memcached->set('master:config_files', serialize($this->configFiles));
 		}
 		else {
 			$conn->Query('REPLACE '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("configs_parsed", '.$conn->qstr(serialize($cache)).', '.adodb_mktime().')');
 			$conn->Query('REPLACE '.TABLE_PREFIX.'Cache (VarName, Data, Cached) VALUES ("config_files", '.$conn->qstr(serialize($this->configFiles)).', '.adodb_mktime().')');
 		}
 
 		$cache_rebuild_by = SERVER_NAME . ' (' . getenv('REMOTE_ADDR') . ') - ' . adodb_date('d/m/Y H:i:s');
 		$conn->Query('REPLACE ' . TABLE_PREFIX . 'Cache (VarName, Data, Cached) VALUES ("last_cache_rebuild", ' . $conn->qstr($cache_rebuild_by) . ', ' . adodb_mktime() . ')');
 
 		unset($this->configFiles);
 	}
 
 	function RestoreParsedData()
 	{
 		$conn =& $this->Application->GetADODBConnection();
 		if (!isset($this->Application->Memcached) || !($data = $this->Application->Memcached->get('master:configs_parsed'))) {
 			$data = $conn->GetOne('SELECT Data FROM '.TABLE_PREFIX.'Cache WHERE VarName = "configs_parsed"');
 		}
 		if ($data) {
 			$cache = unserialize($data);
 			$this->Application->Factory->Files = $cache['Factory.Files'];
 			$this->Application->Factory->realClasses = $cache['Factory.realClasses'];
 			$this->Application->Factory->Dependencies = $cache['Factory.Dependencies'];
 			$this->prefixFiles = $cache['ConfigReader.prefixFiles'];
 
 			$event_manager =& $this->Application->recallObject('EventManager');
 			$event_manager->buildEvents = $cache['EventManager.buildEvents'];
 			$event_manager->beforeRegularEvents = $cache['EventManager.beforeRegularEvents'];
 			$event_manager->afterRegularEvents = $cache['EventManager.afterRegularEvents'];
 			$event_manager->beforeHooks = $cache['EventManager.beforeHooks'];
 			$event_manager->afterHooks = $cache['EventManager.afterHooks'];
 
 			$aggregator =& $this->Application->recallObject('TagsAggregator', 'kArray');
 			$aggregator->_Array = $cache['TagsAggregator.data'];
 
 			$this->Application->ConfigHash = $cache['Application.ConfigHash'];
 
 			$this->Application->Caches['ConfigVariables'] = $cache['Application.ConfigCacheIds'];
 			$this->Application->ConfigCacheIds = $cache['Application.ConfigCacheIds'];
 
 			$this->Application->ReplacementTemplates = $cache['Application.ReplacementTemplates'];
 			$this->Application->RewriteListeners = $cache['Application.RewriteListeners'];
 
 			$this->Application->ModuleInfo = $cache['Application.ModuleInfo'];
 
 			return true;
 
 		}
 		else return false;
 	}
 
 	function ResetParsedData($include_sections = false)
 	{
 		$conn =& $this->Application->GetADODBConnection();
 		$conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName = "configs_parsed"');
 		if (isset($this->Application->Memcached)) {
 			$this->Application->Memcached->delete('master:configs_parsed');
 		}
 
 		if ($include_sections) {
 			$conn->Query('DELETE FROM '.TABLE_PREFIX.'Cache WHERE VarName = "sections_parsed"');
 			if (isset($this->Application->Memcached)) {
 				$this->Application->Memcached->delete('master: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();
 			if ($restored) return;
 		}
 
 		$this->ProcessAllConfigs = true;
 
 		$this->includeConfigFiles($folderPath, $cache);
 		$this->ParseConfigs();
 
 		// tell AfterConfigRead to store cache if neede
 		// can't store it here beacuse AfterConfigRead needs ability to change config data
 		$this->StoreCache = $cache;
 	}
 
 	function findConfigFiles($folderPath, $level = 0)
 	{
 		/*if ($level == 0) {
 			if ($this->Application->isDebugMode()) {
 				$start_time = getmicrotime();
 				$this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . $folderPath . '")');
 				$this->Application->Debugger->appendTrace();
 			}
 		}*/
 
 		// 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);
 		}
 
 		/*if ($level == 0) {
 			if ($this->Application->isDebugMode()) {
 				$this->Application->Debugger->appendHTML('kUnitConfigReader::findConfigFiles("' . FULL_PATH . $folderPath . '"): ' . (getmicrotime() - $start_time));
 			}
 		}*/
 	}
 
 	function includeConfigFiles($folderPath, $cache = true)
 	{
 		$this->Application->refreshModuleInfo();
 
 		$conn =& $this->Application->GetADODBConnection();
 		if (!isset($this->Application->Memcached) || !($data = $this->Application->Memcached->get('master:config_files'))) {
 			$data = $conn->GetOne('SELECT Data FROM ' . TABLE_PREFIX . 'Cache WHERE VarName = "config_files"');
 		}
 
 		if ($cache && $data) {
 			$this->configFiles = unserialize($data);
 			shuffle($this->configFiles);
 		}
 		else {
 			$this->findConfigFiles($folderPath); // search from base directory
 		}
 
 		foreach ($this->configFiles as $filename)
 		{
 			$prefix = $this->PreloadConfigFile($filename);
 			if (!$prefix) {
 				trigger_error('Prefix not defined in config file ' . $filename, E_USER_ERROR);
 			}
 		}
 	}
 
 	/**
 	 * 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) {
 			if (in_array($prefix, $this->AfterConfigProcessed)) {
 				continue;
 			}
 
 			$this->Application->HandleEvent( new kEvent($prefix.':OnAfterConfigRead') );
 			$this->AfterConfigProcessed[] = $prefix;
 		}
 
 		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();
 			$this->CacheParsedData();
 
 			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);
 				}
 			}
 
 			$after_event = new kEvent('adm:OnAfterCacheRebuild');
 			$this->Application->HandleEvent($after_event);
 		}
 	}
 
 	/**
 	 * 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);
+		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->includeConfigFiles(MODULES_PATH, false);
 		$this->ParseConfigs();
 		$this->AfterConfigRead(false);
 		$this->processDynamicClones();
 		$this->retrieveCollections();
 	}
 
 	/**
 	 * 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) {
 			if (in_array($prefix, $this->AfterConfigProcessed)) {
 				continue;
 			}
 
 			$this->Application->HandleEvent( new kEvent($prefix.':OnAfterConfigRead') );
 			$this->AfterConfigProcessed[] = $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_listener = $config['RewriteListener'];
 				if (strpos($rewrite_listener, ':') === false) {
 					$rewrite_listener = $prefix . '_EventHandler:' . $rewrite_listener;
 				}
 
 				$rewrite_priority = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false;
 
 				$this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listener, 'priority' => $rewrite_priority);
 			}
 		}
 	}
 
 	/**
 	 * Register nessasary classes
 	 * This method should only process the data which is cached!
 	 *
 	 * @param string $prefix
 	 * @access private
 	 */
 	function parseConfig($prefix)
 	{
 		$config =& $this->configData[$prefix];
 		$event_manager =& $this->Application->recallObject('EventManager');
 		/* @var $event_manager kEventManager */
 
 		$register_classes = getArrayValue($config,'RegisterClasses');
 		if (!$register_classes) $register_classes = Array();
 		$class_params=Array('ItemClass','ListClass','EventHandlerClass','TagProcessorClass');
 		foreach($class_params as $param_name)
 		{
 			if ( !(isset($config[$param_name]) ) ) continue;
 			$config[$param_name]['pseudo'] = $this->getPrefixByParamName($param_name,$prefix);
 			$register_classes[] = $config[$param_name];
 		}
 
 		foreach($register_classes as $class_info)
 		{
 			$require_classes = getArrayValue($class_info, 'require_classes');
 			if ($require_classes) {
 				if (!is_array($require_classes)) {
 					$require_classes = array($require_classes);
 				}
 				if (!isset($config['_Dependencies'][$class_info['class']])) {
 					$config['_Dependencies'][$class_info['class']] = array();
 				}
 				$config['_Dependencies'][$class_info['class']] = array_merge($config['_Dependencies'][$class_info['class']], $require_classes);
 			}
 
 			$this->Application->registerClass(
 				$class_info['class'],
 				$config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'],
 				$class_info['pseudo']/*,
 				getArrayValue($class_info, 'require_classes')*/
 			);
 			if (getArrayValue($class_info, 'build_event')) {
 				$event_manager->registerBuildEvent($class_info['pseudo'],$class_info['build_event']);
 			}
 		}
 
 		$regular_events = getArrayValue($config, 'RegularEvents');
 		if ($regular_events) {
 			foreach ($regular_events as $short_name => $regular_event_info) {
 				$event_status = array_key_exists('Status', $regular_event_info) ? $regular_event_info['Status'] : STATUS_ACTIVE;
 				$event_manager->registerRegularEvent( $short_name, $config['Prefix'].':'.$regular_event_info['EventName'], $regular_event_info['RunInterval'], $regular_event_info['Type'], $event_status);
 			}
 		}
 
 		$hooks = getArrayValue($config, 'Hooks');
 		if (is_array($hooks) && count($hooks) > 0) {
 			foreach ($hooks as $hook) {
 				if (isset($config['ParentPrefix']) && $hook['HookToPrefix'] == $config['ParentPrefix']) {
 					trigger_error('Depricated Hook Usage [prefix: <b>'.$config['Prefix'].'</b>; do_prefix: <b>'.$hook['DoPrefix'].'</b>] use <b>#PARENT#</b> as <b>HookToPrefix</b> value, where HookToPrefix is same as ParentPrefix', E_USER_NOTICE);
 				}
 
 				if ($hook['HookToPrefix'] == '') {
 					$hook['HookToPrefix'] = $config['Prefix']; // new: set hooktoprefix to current prefix if not set
 				}
 
 				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#') {
 					continue; // we need parent prefix but it's not set !
 				}
 
 				$do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix'];
 
 				if ( !is_array($hook['HookToEvent']) ) {
 					$hook_events = Array( $hook['HookToEvent'] );
 				}
 				else {
 					$hook_events = $hook['HookToEvent'];
 				}
 				foreach ($hook_events as $hook_event) {
 					$this->Application->registerHook($hook['HookToPrefix'], $hook['HookToSpecial'], $hook_event, $hook['Mode'], $do_prefix, $hook['DoSpecial'], $hook['DoEvent'], $hook['Conditional']);
 				}
 			}
 		}
 
 		if ( is_array(getArrayValue($config, 'AggregateTags')) ) {
 			foreach ($config['AggregateTags'] 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', 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');
 		$conn =& $this->Application->GetADODBConnection();
 
 		$table_found = $conn->Query('SHOW TABLES LIKE "'.$tablename.'"');
 		if (!$table_found) {
 			// config present, but table missing, strange
 			$debugger->appendHTML("<b class='debug_error'>Config Warning: </b>Table <strong>$tablename</strong> missing, but prefix <b>".$config['Prefix']."</b> requires it!");
 			safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 			return ;
 		}
 
 		$res = $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)',
 			'type_missing' => '<strong>Type definition</strong> for field <strong>%s</strong> missing in config',
 		);
 
 		$config_errors = Array ();
 		$tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix
 
 		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 {
 					if (is_numeric($field['Default'])) {
 						$field['Default'] = preg_match('/[\.,]/', $field['Default']) ? (float)$field['Default'] : (int)$field['Default'];
 					}
 
 					$options = $config['Fields'][$f_name];
 					$default_missing = 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 (isset($options['not_null']) && $options['not_null'] && !isset($options['default']) ) {
 							$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
 						}
 					}
 					else {
 						if (isset($options['not_null']) && $options['not_null']) {
 							$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
 						}
 					}
 
 					if (!array_key_exists('type', $options)) {
 						$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
 					}
 
 					if (!$default_missing) {
 						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 ($options['default'] != '#NOW#' && $field['Default'] !== $options['default'] && !in_array($options['type'], $float_types)) {
 							$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
 						}
 					}
 				}
 			}
 		}
 
 		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);
 
 			$debugger->appendHTML($config_errors);
 			safeDefine('DBG_RAISE_ON_WARNINGS', 1);
 		}
 	}
 
 	function varDump($value)
 	{
 		return '<strong>'.var_export($value, true).'</strong> of '.gettype($value);
 	}
 
 	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] = array_merge_recursive2($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->_directorySeparator . '(.*)' . $this->_directorySeparator . '#U', $filename, $rets);
 				$config['ModuleFolder'] = $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' ? '' : $rets[1] . '/';
 					$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])) {
 			if ($this->Application->isDebugMode()) $this->Application->Debugger->appendTrace();
 			trigger_error('Configuration file for prefix <b>'.$prefix.'</b> is unknown', E_USER_ERROR);
 			return ;
 		}
 		$file = $this->prefixFiles[$prefix];
 		$prefix = $this->PreloadConfigFile($file);
 
 		$clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
 		$clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones);
 
 		if ($this->FinalStage) {
 			array_unshift($clones, $prefix);
 			$clones = array_unique($clones);
 			foreach ($clones as $a_prefix) {
 				$this->Application->HandleEvent( new kEvent($a_prefix.':OnAfterConfigRead') );
 			}
 		}
 	}
 
 	/**
 	 * 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;
 		}
 
 	}
 
 	function getPrefixByParamName($paramName,$prefix)
 	{
 		$pseudo_class_map=Array(
 		'ItemClass'=>'%s',
 		'ListClass'=>'%s_List',
 		'EventHandlerClass'=>'%s_EventHandler',
 		'TagProcessorClass'=>'%s_TagProcessor'
 		);
 		return sprintf($pseudo_class_map[$paramName],$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)
 	{
 		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 . 'plugins' . $this->_directorySeparator . '|' . $this->_directorySeparator . 'core#', $config_path)) {
 			// always allow to include configs from core and plugins folder
 			return true;
 		}
 
 		$module_found = false;
 		if (!$this->Application->ModuleInfo) {
 			return false;
 		}
 
 		foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 			$module_path = DIRECTORY_SEPARATOR . trim($module_info['Path'], '/') . DIRECTORY_SEPARATOR;
 
 			// config file path starts with module folder path
 			if (substr($config_path, 0, strlen($module_path)) == $module_path) {
 //				if (preg_match('#^' . preg_quote($module_path, '/') . '#', $config_path)) {
 				$module_found = true;
 				break;
 			}
 		}
 
 		return $module_found;
 	}
 
 	/**
 	 * 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.0.x/core/kernel/application.php
===================================================================
--- branches/5.0.x/core/kernel/application.php	(revision 13241)
+++ branches/5.0.x/core/kernel/application.php	(revision 13242)
@@ -1,3012 +1,3012 @@
 <?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.
 */
 
 /**
 * 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.
 * @package kernel4
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class kApplication {
 
 	/**
 	 * 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();
 
 
 	// 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 to itself, needed because everybody used to write $this->Application, even inside kApplication
 	 *
 	 * @var kApplication
 	 */
 	var $Application = null;
 
 	/**
 	 * Ref for TemplatesChache
 	 *
 	 * @var TemplatesCache
 	 */
 	var $TemplatesCache = null;
 
 	/**
 	 * Physical template name mapping to their template names based on structure
 	 *
 	 * @var Array
 	 */
 	var $structureTemplateMapping = Array ();
 
 	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;
 
 	/**
 	 * Memcache object pointer
 	 *
 	 * @var Memcache
 	 */
 	var $Memcached = 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;
 
 	/**
 	* 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
 	*/
 	function &Instance()
 	{
 		static $instance = false;
 
 		if (!$instance) {
 			$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
 			$instance = new $class();
 			$instance->Application =& $instance;
 		}
 
 		return $instance;
 	}
 
 	function InitMemcached()
 	{
 		return ;
 
 		$memcached_servers = 'localhost:11211'; // $this->Application->ConfigValue('MemcachedServers');
 
 		if ($memcached_servers && class_exists('Memcache')) {
 			$this->Memcached = new Memcache();
 			$servers = explode(';', $memcached_servers);
 			foreach ($servers as $server) {
 				list ($server, $port) = strpos($server, ':') !== false ? explode(':', $server, 2) : Array ($server, 11211);
 				$this->Memcached->addServer($server, $port);
 			}
 		}
 
 		//try to set something to cache, if not working - set $this->Memcached to null
 	}
 
 	function CacheSet($name, $value, $expiration)
 	{
 		if (isset($this->Memcached)) {
 			return $this->Memcached->set($name, $value, 0, $expiration);
 		}
 		return false;
 	}
 
 	function CacheGet($name)
 	{
 		if (isset($this->Memcached)) {
 			return $this->Memcached->get($name);
 		}
 		return false;
 	}
 
 	/**
 	* Initializes the Application
  	*
  	* @access public
 	* @see kHTTPQuery
 	* @see Session
 	* @see TemplatesCache
 	* @return bool Was Init actually made now or before
 	*/
 	function Init()
 	{
 		if($this->InitDone) {
 			return false;
 		}
 
 		$this->isAdmin = constOn('ADMIN');
 
 		$this->InitMemcached();
 
 		if (!constOn('SKIP_OUT_COMPRESSION')) {
 			ob_start(); // collect any output from method (other then tags) into buffer
 		}
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
 			$this->Debugger->appendMemoryUsage('Application before Init:');
 		}
 		if (!$this->isDebugMode() && !constOn('DBG_ZEND_PRESENT')) {
 			error_reporting(0);
 			ini_set('display_errors', 0);
 		}
 		if (!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;
 			}
 		}
 
 		$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->EventManager =& $this->Factory->makeClass('EventManager');
 		$this->Factory->Storage['EventManager'] =& $this->EventManager;
 		$this->RegisterDefaultBuildEvents();
 		$this->SetDefaultConstants();
 
 		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->HttpQuery->AfterInit();
 
 		$this->Session->ValidateExpired();
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
 		}
 
 		$this->LoadCache();
 		$this->InitConfig();
 
 		$this->Phrases->Init('phrases');
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('Loaded cache and phrases');
 		}
 
 		$this->ValidateLogin();
 
 		$this->UnitConfigReader->AfterConfigRead();
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->Debugger->appendTimestamp('Processed AfterConfigRead');
 		}
 
 		/*// Module items are recalled during url parsing & PhrasesCache is needed already there,
 		// because it's used in their build events. That's why phrases cache initialization is
 		// called from kHTTPQuery in case when mod_rewrite is used
 		if (!$this->RewriteURLs()) {
 			$this->Phrases = new PhrasesCache();
 		}*/
 
 		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
 		}
 
 		$this->SetVar('visits_id', $this->RecallVar('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 */
 
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'Modules
 				WHERE Loaded = 1
 				ORDER BY LoadOrder';
 		$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
 
 		$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) {
 			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');
 		}
 		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');
 		}
 		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)
 	{
 		static $language_info = null;
 
 		if (!isset($language_info)) {
 			// cache primary language info first
 			$table = $this->getUnitOption('lang', 'TableName');
 			$id_field = $this->getUnitOption('lang', 'IDField');
 
 			$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');
 		}
 
 		$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 (constOn('DBG_FORCE_THEME')) {
 			$theme_id = DBG_FORCE_THEME;
 		}
 		elseif (!$force_front && $this->isAdmin) {
 			$theme_id = 999;
 		}
 		else {
 			$table = $this->getUnitOption('theme','TableName');
 			$id_field = $this->getUnitOption('theme','IDField');
 			$sql = 'SELECT '.$id_field.'
 					FROM '.$table.'
 					WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
 			$theme_id = $this->Conn->GetOne($sql);
 		}
 
 		return $theme_id;
 	}
 
 	function GetPrimaryCurrency()
 	{
 		if ($this->isModuleEnabled('In-Commerce')) {
 			$table = $this->getUnitOption('curr', 'TableName');
 			return $this->Conn->GetOne('SELECT ISO FROM '.$table.' WHERE IsPrimary = 1');
 		}
 		else {
 			return 'USD';
 		}
 	}
 
 	/**
 	* Registers default classes such as ItemController, GridController and LoginController
 	*
 	* Called automatically while initializing Application
 	* @access private
 	* @return void
 	*/
 	function RegisterDefaultClasses()
 	{
 		$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
 		$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
 		$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('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.php');
 		$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
 		$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session.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')) {
 			$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()
 	{
 		$event_manager =& $this->recallObject('EventManager');
 		$event_manager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
 	}
 
 	/**
 	 * Returns item's filename that corresponds id passed. If possible, then get it from cache
 	 *
 	 * @param string $prefix
 	 * @param int $id
 	 * @return string
 	 */
 	function getFilename($prefix, $id, $category_id=null)
 	{
 		$filename = $this->getCache('filenames',  $prefix.'_'.$id);
 		if ($filename === false) {
 			$table = $this->getUnitOption($prefix, 'TableName');
 			$id_field = $this->getUnitOption($prefix, 'IDField');
 
 			if ($prefix == 'c') {
 				if(!$id) {
 					$this->setCache('filenames', $prefix.'_'.$id, '');
 					return '';
 				}
 
 				// this allows to save 2 sql queries for each category
 				$sql = 'SELECT NamedParentPath, CachedTemplate, TreeLeft, TreeRight
 						FROM '.$table.'
 						WHERE '.$id_field.' = '.$this->Conn->qstr($id);
 				$category_data = $this->Conn->GetRow($sql);
 
 				// only direct links to category pages work (symlinks, container pages and so on won't work)
 				$filename = $category_data['NamedParentPath'];
 				$this->setCache('category_templates', $id, $filename);
 				$this->setCache('category_designs', $id, ltrim($category_data['CachedTemplate'], '/'));
 				$this->setCache('category_tree', $id, $category_data['TreeLeft'] . ';' . $category_data['TreeRight']);
 			}
 			else {
 				$sql = 'SELECT ResourceId
 						FROM ' . $table . '
 						WHERE ' . $id_field . ' = ' . $this->Conn->qstr($id);
 				$resource_id = $this->Conn->GetOne($sql);
 
 				if (is_null($category_id)) {
 					$category_id = $this->GetVar('m_cat_id');
 				}
 
 				$sql = 'SELECT Filename
 						FROM ' . TABLE_PREFIX . 'CategoryItems
 						WHERE (ItemResourceId = ' . $resource_id . ') AND (CategoryId = ' . (int)$category_id . ')';
 				$filename = $this->Conn->GetOne($sql);
 
 				/*if (!$filename) {
 					$sql = 'SELECT Filename
 							FROM ' . TABLE_PREFIX . 'CategoryItems
 							WHERE ItemResourceId = ' . $resource_id . ' AND PrimaryCat = 1';
 					$filename = $this->Conn->GetOne($sql);
 				}
 
 				$sql = 'SELECT Filename
 						FROM ' . $table . '
 						WHERE ' . $id_field . ' = ' . $this->Conn->qstr($id);
 				$filename = $this->Conn->GetOne($sql);*/
 			}
 			$this->setCache('filenames', $prefix.'_'.$id, $filename);
 		}
 		return $filename;
 	}
 
 
 	/**
 	 * Adds new value to cache $cache_name and identified by key $key
 	 *
 	 * @param string $cache_name cache name
 	 * @param int $key key name to add to cache
 	 * @param mixed $value value of chached record
 	 */
 	function setCache($cache_name, $key, $value, $expiration=3600)
 	{
 		$cache =& $this->recallObject('Cache');
 		/* @var $cache kCache */
 
 		return $cache->setCache($cache_name, $key, $value, $expiration);
 	}
 
 	/**
 	 * Returns cached $key value from cache named $cache_name
 	 *
 	 * @param string $cache_name cache name
 	 * @param int $key key name from cache
 	 * @return mixed
 	 */
 	function getCache($cache_name, $key)
 	{
 		$cache =& $this->recallObject('Cache');
 		return $cache->getCache($cache_name, $key);
 	}
 
 	/**
 	* Defines default constants if it's not defined before - in config.php
 	*
 	* @access private
 	*/
 	function SetDefaultConstants() // it's defined in startup.php - can be removed??
 	{
 		safeDefine('SERVER_NAME', $_SERVER['HTTP_HOST']);
 	}
 
 	/**
 	 * Registers each module specific constants if any found
 	 *
 	 */
 	function registerModuleConstants()
 	{
 		if (file_exists(KERNEL_PATH.'/constants.php')) {
 			k4_include_once(KERNEL_PATH.'/constants.php');
 		}
 
 		if (!$this->ModuleInfo) return false;
 		foreach($this->ModuleInfo as $module_name => $module_info)
 		{
 			$module_path = '/'.$module_info['Path'];
 			$contants_file = FULL_PATH.$module_path.'constants.php';
 			if( file_exists($contants_file) ) k4_include_once($contants_file);
 		}
 		return true;
 	}
 
 	function ProcessRequest()
 	{
 		$event_manager =& $this->recallObject('EventManager');
 		/* @var $event_manager kEventManager */
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_SHOW_HTTPQUERY')) {
 			$this->Debugger->appendHTML('HTTPQuery:');
 			$this->Debugger->dumpVars($this->HttpQuery->_Params);
 		}
 
 		$event_manager->ProcessRequest();
 		$event_manager->RunRegularEvents(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() && 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->Application->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->ModuleInfo['Core']['RootCat'])) {
 				// user can edit cms blocks
 				$editing_mode = $this->GetVar('editing_mode');
 				define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
 				$this->Phrases->setPhraseEditing();
 			}
 		}
 
 		safeDefine('EDITING_MODE', ''); // user can't edit anything
 
 		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() && constOn('DBG_PROFILE_MEMORY')) {
 			$this->Debugger->appendMemoryUsage('Application before Parsing:');
 		}
 
 		$this->HTML = $this->Parser->Run($t);
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode() && 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') );
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode() && constOn('DBG_PROFILE_MEMORY')) {
 			$this->Debugger->appendMemoryUsage('Application before Done:');
 		}
 
 		if (defined('DEBUG_MODE') && $this->isDebugMode()) {
 			$this->EventManager->RunRegularEvents(reAFTER);
 			$this->Session->SaveData();
 
 			if (constOn('DBG_CACHE')) {
 				$cache =& $this->recallObject('Cache');
 				$cache->printStatistics();
 			}
 
 			$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
 		}
 		else {
 			$this->HTML = ob_get_clean().$this->HTML;
 		}
 
 		if ($this->UseOutputCompression()) {
 			header('Content-Encoding: gzip');
 			$compression_level = $this->ConfigValue('OutputCompressionLevel');
 			if ($compression_level < 0 || $compression_level > 9) $compression_level = 7;
 			echo gzencode($this->HTML, $compression_level);
 		}
 		else {
 			echo $this->HTML;
 		}
 
 		$this->UpdateCache();
 
 		flush();
 		if (!$this->isDebugMode()) {
 			$this->EventManager->RunRegularEvents(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 = getmicrotime() - $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 (constOn('IS_INSTALL') || constOn('DBG_ZEND_PRESENT') || 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)
 	{
 		$this->Session->StorePersistentVar($var, $val);
 	}
 
 	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);
 	}
 
 	/**
 	 * 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)
 	{
 		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');
 			$cookie_url = trim($session->CookieDomain.$session->CookiePath, '/.');
 			if ($ssl) {
 				$target_url = defined('ADMIN') && ADMIN ? $this->ConfigValue('AdminSSL_URL') : false;
 				if (!$target_url) {
 					$target_url = $this->ConfigValue('SSL_URL');
 				}
 			}
 			else {
 				$target_url = 'http://'.DOMAIN.$this->ConfigValue('Site_Path');
 			}
 
 			if (!preg_match('#'.preg_quote($cookie_url).'#', $target_url)) {
 				$session->SetMode(smGET_ONLY);
 			}
 		}
 
 		if (isset($params['opener']) && $params['opener'] == 'u') {
 			$wid = $this->Application->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->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->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_recursive2($this->getPassThroughVariables($params), $params);
 		$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 $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;
 	}
 
 
 	/**
 	 * 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, 1);
+			list ($prefix_only, ) = explode('.', $prefix, 2);
 			$sorted[$prefix] = $this->getUnitOption($prefix_only, 'RewritePriority', 0);
 		}
 
-		arsort($sorted);
+		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)
 	{
 //		$session =& $this->recallObject('Session');
 		$force_admin = getArrayValue($params,'admin') || $this->GetVar('admin');
 
 //		if($force_admin) $sid = $this->GetSID();
 
 		$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 ($catalog_item_found || !$cat_processed || !defined('EXP_DIR_URLS')) {
 				// this catalog item detail page OR there is no category given
 				$ret = trim($ret, '/') . '.html';
 			}
 			else {
 				// url ends with "/" and not with ".html"
 				$ret = trim($ret, '/') .  '/';
 			}
 
 			if ($env) {
 				$params[ENV_VAR_NAME] = ltrim($env, ':');
 			}
 		}
 
 		unset($params['pass'], $params['opener'], $params['m_event']);
 		if ($force_admin) {
 			$params['admin'] = 1;
 		}
 
 		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];
 
 		$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;
 	}
 
 	function BuildEnv($t, $params, $pass='all', $pass_events = false, $env_var = true)
 	{
 		$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__') {
 			// to put category & item templates into cache
 			$filename = $this->getFilename('c', $category_id);
 			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->getCache('item_templates', $category_id);
 			}
 			elseif ($category_id) {
 				$t = strtolower( preg_replace('/^Content\//i', '', $this->getCache('category_templates', $category_id)) );
 			}
 			else {
 				$t = 'index';
 			}
 		}
 
 		$ret .= $t.':'.$this->BuildModuleEnv('m', $params, $pass_events).$env_string;
 
 		unset($params['pass'], $params['opener'], $params['m_event']);
 
 		if ($this->GetVar('admin') && !isset($params['admin'])) {
 			$params['admin'] = 1;
 
 			if (!array_key_exists('editing_mode', $params)) {
 				$params['editing_mode'] = EDITING_MODE;
 			}
 		}
 
 		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;
 	}
 
 	function BaseURL($prefix='', $ssl=null)
 	{
 		if ($ssl === null) {
 			return PROTOCOL.SERVER_NAME.(defined('PORT')?':'.PORT : '').rtrim(BASE_PATH, '/').$prefix.'/';
 		}
 		else {
 			if ($ssl) {
 				$base_url = defined('ADMIN') && ADMIN ? $this->ConfigValue('AdminSSL_URL') : false;
 				if (!$base_url) {
 					$base_url = $this->ConfigValue('SSL_URL');
 				}
 
 				return rtrim($base_url, '/').$prefix.'/';
 			}
 			else {
 				return 'http://'.DOMAIN.(defined('PORT')?':'.PORT : '').rtrim( $this->ConfigValue('Site_Path'), '/').$prefix.'/';
 			}
 		}
 	}
 
 	function Redirect($t='', $params=null, $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']);
 		}
 
 		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);
 
 		$a_location = $location;
 		$location = "Location: $location";
 
 		if ($this->isDebugMode() && constOn('DBG_REDIRECT')) {
 			$this->Debugger->appendTrace();
 			echo "<b>Debug output above!!!</b> Proceed to redirect: <a href=\"$a_location\">$a_location</a><br>";
 		}
 		else {
 			if ($js_redirect) {
 				$this->SetVar('t', 'redirect');
 				$this->SetVar('redirect_to_js', addslashes($a_location) );
 				$this->SetVar('redirect_to', $a_location);
 				return true;
 			}
 			else {
 				if ($this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t')) {
 					// redirection to other then current template during ajax request
 					echo '#redirect#'.$a_location;
 				}
 				elseif (headers_sent() != '') {
 					// some output occured -> redirect using javascript
 					echo '<script type="text/javascript">window.location.href = \''.$a_location.'\';</script>';
 				}
 				else {
 					// no output before -> redirect using HTTP header
 
 //					header('HTTP/1.1 302 Found');
 					header("$location");
 				}
 			}
 		}
 
 		ob_end_flush();
 		// session expiration is called from session initialization,
 		// that's why $this->Session may be not defined here
 		$session =& $this->Application->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)
 	{
 		// !!!!!!!!
 //		if( !is_object($this->Phrases) ) $this->Debugger->appendTrace();
 		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 != -1) {
 			$user_id = -2;
 		}
 
 		$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 == -2); // 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 != -2) && constOn('DBG_REQUREST_LOG') ) {
 			$http_query =& $this->recallObject('HTTPQuery');
 			$http_query->writeRequestLog(DBG_REQUREST_LOG);
 		}
 
 		if ($user_id != -2) {
 			// normal users + root
 			$this->LoadPersistentVars();
 		}
 	}
 
 	/**
 	 * Loads current user persistent session data
 	 *
 	 */
 	function LoadPersistentVars()
 	{
 		$this->Session->LoadPersistentVars();
 	}
 
 	function LoadCache() {
 		$cache_key = $this->GetVar('t').$this->GetVar('m_theme').$this->GetVar('m_lang').$this->isAdmin;
 		$query = sprintf("SELECT PhraseList, ConfigVariables FROM %s WHERE Template = %s",
 											TABLE_PREFIX.'PhraseCache',
 											$this->Conn->qstr(md5($cache_key)));
 		$res = $this->Conn->GetRow($query);
 
 		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->Caches['ConfigVariables'] = $config_ids;
 		$this->ConfigCacheIds = $config_ids;
 	}
 
 	function LoadStructureTemplateMapping()
 	{
 		// get template mapping
 		$sql = 'SELECT Data
 				FROM ' . TABLE_PREFIX . 'Cache
 				WHERE VarName = "template_mapping"';
 		$template_mapping = $this->Conn->GetOne($sql);
 
 		if (!$this->isAdmin && $template_mapping) {
 			// template mappings only for Front-End
 			$this->structureTemplateMapping = unserialize($template_mapping);
 		}
 	}
 
 	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) {
 			$this->ConfigHash = array_merge($this->ConfigHash, $this->Conn->GetCol(
 				'SELECT VariableValue, VariableName FROM '.TABLE_PREFIX.'ConfigurationValues
 				 WHERE VariableId IN ('.implode(',', $this->Caches['ConfigVariables']).')', 'VariableName'));
 		}
 	}
 
 	/**
 	 * Returns configuration option value by name
 	 *
 	 * @param string $name
 	 * @return string
 	 */
 	function ConfigValue($name)
 	{
 		$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;
 		}
 
 		$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) {
 
 		}
 	}
 
 	/**
 	 * Allows to process any type of event
 	 *
 	 * @param kEvent $event
 	 * @access public
 	 * @author Alex
 	 */
 	function HandleEvent(&$event, $params=null, $specificParams=null)
 	{
 		if ( isset($params) ) {
 			$event = new kEvent( $params, $specificParams );
 		}
 
 		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);
 	}
 
 	/**
 	 * 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 $hookto_prefix
 	 * @param string $hookto_special
 	 * @param string $hookto_event
 	 * @param string $mode
 	 * @param string $do_prefix
 	 * @param string $do_special
 	 * @param string $do_event
 	 * @param string $conditional
 	 * @access public
 	 * @todo take care of a lot parameters passed
 	 * @author Kostja
 	 */
 	function registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $conditional)
 	{
 		$event_manager =& $this->recallObject('EventManager');
 		$event_manager->registerHook($hookto_prefix, $hookto_special, $hookto_event, $mode, $do_prefix, $do_special, $do_event, $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
 	 * @return Object
 	 * @author Alex
 	 */
 	function &recallObject($name,$pseudo_class=null,$event_params=Array())
 	{
 		$result =& $this->Factory->getObject($name, $pseudo_class, $event_params);
 		return $result;
 	}
 
 	/**
 	 * Returns object using Variable number of params,
 	 * all params starting with 4th are passed to object consturctor
 	 *
 	 * @param string $name
 	 * @param string $pseudo_class
 	 * @param Array $event_params
 	 * @return Object
 	 * @author Alex
 	 */
 	function &recallObjectP($name,$pseudo_class=null,$event_params=Array())
 	{
 		$func_args = func_get_args();
 		$result =& ref_call_user_func_array( Array(&$this->Factory, 'getObjectP'), $func_args );
 		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)
 	{
 		$this->Factory->DestroyObject($name);
 	}
 
 	/**
 	 * Get's real class name for pseudo class,
 	 * includes class file and creates class
 	 * instance
 	 *
 	 * @param string $pseudo_class
 	 * @return Object
 	 * @access public
 	 * @author Alex
 	 */
 	function &makeClass($pseudo_class)
 	{
 		$func_args = func_get_args();
 		$result =& ref_call_user_func_array( Array(&$this->Factory, 'makeClass'), $func_args);
 
 		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)
 	{
 		$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 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)
 	{
 		/*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)
 	{
 //		$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)
 	{
 //		$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)
 	{
 		/*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)
 	{
 		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)
 	{
 		$event_manager =& $this->recallObject('EventManager');
 		$event_manager->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) )
 		{
 			$errorLevel = constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ? E_USER_ERROR : E_USER_WARNING;
 			$this->Debugger->appendTrace();
 
 			$error_msg = '<span class="debug_error">'.$msg.' ('.$code.')</span><br><a href="javascript:$Debugger.SetClipboard(\''.htmlspecialchars($sql).'\');"><b>SQL</b></a>: '.$this->Debugger->formatSQL($sql);
 			$long_id = $this->Debugger->mapLongError($error_msg);
 			trigger_error( mb_substr($msg.' ('.$code.') ['.$sql.']',0,1000).' #'.$long_id, $errorLevel);
 			return true;
 		}
 		else
 		{
 			//$errorLevel = constOn('IS_INSTALL') ? E_USER_WARNING : E_USER_ERROR;
 			$errorLevel = E_USER_WARNING;
 			trigger_error('<b>SQL Error</b> in sql: '.$sql.', code <b>'.$code.'</b> ('.$msg.')', $errorLevel);
 			/*echo '<b>xProcessing SQL</b>: '.$sql.'<br>';
 			echo '<b>Error ('.$code.'):</b> '.$msg.'<br>';*/
 			return $errorLevel == E_USER_ERROR ? false : 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 = '')
 	{
 		if (defined('SILENT_LOG') && SILENT_LOG) {
 			if ( !(defined('DBG_IGNORE_STRICT_ERRORS') && DBG_IGNORE_STRICT_ERRORS && defined('E_STRICT') && ($errno == E_STRICT)) ) {
 				$fp = fopen(FULL_PATH.'/silent_log.txt','a');
 				$time = adodb_date('d/m/Y H:i:s');
 				fwrite($fp, '['.$time.'] #'.$errno.': '.strip_tags($errstr).' in ['.$errfile.'] on line '.$errline."\n");
 				fclose($fp);
 			}
 		}
 
 		$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)
 			$ignore_fatal_errors = defined('DBG_IGNORE_FATAL_ERRORS') && DBG_IGNORE_FATAL_ERRORS;
 
 			if (($errno == E_USER_ERROR) && !$ignore_fatal_errors) {
 				echo ('	<div style="background-color: #FEFFBF; margin: auto; padding: 10px; border: 2px solid red; text-align: center">
 							<strong>Fatal Error: </strong>'."$errstr in $errfile on line $errline".'
 						</div>');
 				exit;
 			}
 
 			if (!$this->errorHandlers) {
 				return true;
 			}
 		}
 
 		$res = false;
 		$i = 0; // while (not foreach) because it is array of references in some cases
 		$eh_count = count($this->errorHandlers);
 		while ($i < $eh_count) {
 			if ( is_array($this->errorHandlers[$i]) ) {
 				$object =& $this->errorHandlers[$i][0];
 				$method = $this->errorHandlers[$i][1];
 				$res = $object->$method($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 			else {
 				$function = $this->errorHandlers[$i];
 				$res = $function($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 			$i++;
 		}
 		return $res;
 	}
 
 	/**
 	 * 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 unknown
 	 */
 	function &EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = false)
 	{
 		$event =& $this->EmailEvent($email_event_name, 1, $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 unknown
 	 */
 	function &EmailEventUser($email_event_name, $to_user_id = null, $send_params = false)
 	{
 		$event =& $this->EmailEvent($email_event_name, 0, $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 unknown
 	 */
 	function &EmailEvent($email_event_name, $email_event_type, $to_user_id = -1, $send_params = false)
 	{
 		$params = array(
 			'EmailEventName' => $email_event_name,
 			'EmailEventToUserId' => $to_user_id,
 			'EmailEventType' => $email_event_type,
 		);
 		if ($send_params) {
 			$params['DirectSendParams'] = $send_params;
 		}
 		$event_str = isset($send_params['use_special']) ? 'emailevents.'.$send_params['use_special'].':OnEmailEvent' : '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');
 		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;
 
 	}
 
 	function reportError($class, $method)
 	{
 		$this->Debugger->appendTrace();
 		trigger_error('depricated method <b>'.$class.'->'.$method.'(...)</b>', E_USER_ERROR);
 	}
 
 	/**
 	 * 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 = '')
 	{
 		if (preg_match('/prefix:(.*)/', $wid, $regs)) {
 			$wid = $this->GetTopmostWid($regs[1]);
 		}
 
 		return TABLE_PREFIX.'ses_'.$this->GetSID().($wid ? '_'.$wid : '').'_edit_'.$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)
 	{
 		return preg_match('/'.TABLE_PREFIX.'ses_'.$this->GetSID().'(_[\d]+){0,1}_edit_(.*)/',$table);
 	}
 
 	/**
 	 * Checks, that given prefix is in temp mode
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
 	function IsTempMode($prefix, $special = '')
 	{
 		$top_prefix = $this->Application->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->Application->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 TimeZoneAdjustment($time_zone = null)
 	{
 		if ($time_zone == 'GMT') {
 			return (-1) * adodb_date('Z');
 		}
 		$target_zone = isset($time_zone) ? $time_zone : $this->ConfigValue('Config_Site_Time');
 		return 3600 * ($target_zone - $this->ConfigValue('Config_Server_Time'));
 	}
 
 	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 -1:
 				$user_groups = $this->ConfigValue('User_LoggedInGroup');
 				break;
 
 			case -2:
 				$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->Application->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 (constOn('IS_INSTALL')) {
 			return ;
 		}
 
 		$count_helper =& $this->Application->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)
 	{
 		$category_template = $this->getFilename('c', $category_id); // to rebuild "category_tree" cache
 
 		$tree_index = $this->getCache('category_tree', $category_id);
 
 		if ($tree_index) {
 			$ret = Array ();
 			list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
 
 			return $ret;
 		}
 
 		return false;
 	}
 
 }
\ No newline at end of file