Index: branches/5.2.x/core/kernel/db/db_load_balancer.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_load_balancer.php	(revision 15238)
+++ branches/5.2.x/core/kernel/db/db_load_balancer.php	(revision 15239)
@@ -1,656 +1,659 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class kDBLoadBalancer extends kBase {
 
 	/**
 	 * Current database type
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected $dbType = 'mysql';
 
 	/**
 	 * Function to handle sql errors
 	 *
 	 * @var string
 	 * @access public
 	 */
 	public $errorHandler = '';
 
 	/**
 	 * Database connection settings
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $servers = Array ();
 
 	/**
 	 * Load of each slave server, given
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $serverLoads = Array ();
 
 	/**
 	 * Caches replication lag of servers
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $serverLagTimes = Array ();
 
 	/**
 	 * Connection references to opened connections
 	 *
 	 * @var Array
 	 * @access protected
 	 */
 	protected $connections = Array ();
 
 	/**
 	 * Index of last user slave connection
 	 *
 	 * @var int
 	 * @access protected
 	 */
 	protected $slaveIndex = false;
 
 	/**
 	 * Index of the server, that was used last
 	 *
 	 * @var int
 	 * @access protected
 	 */
 	protected $lastUsedIndex = 0;
 
 	/**
 	 * Consider slave down if it isn't responding for N milliseconds
 	 *
 	 * @var int
 	 * @access protected
 	 */
 	protected $DBClusterTimeout = 10;
 
 	/**
 	 * Scale load balancer polling time so that under overload conditions, the database server
 	 * receives a SHOW STATUS query at an average interval of this many microseconds
 	 *
 	 * @var int
 	 * @access protected
 	 */
 	protected $DBAvgStatusPoll = 2000;
 
 	/**
 	 * Indicates, that next database query could be cached, when memory caching is enabled
 	 *
 	 * @var bool
 	 * @access public
 	 */
 	public $nextQueryCachable = false;
 
 	/**
 	 * Indicates, that next query should be sent to maser database
 	 *
 	 * @var bool
 	 */
 	public $nextQueryFromMaster = false;
 
 	/**
 	 * Creates new instance of load balancer class
 	 *
 	 * @param string $dbType
 	 * @param Array|string $errorHandler
 	 */
 	function __construct($dbType, $errorHandler = '')
 	{
 		parent::__construct();
 
 		$this->dbType = $dbType;
 		$this->errorHandler = $errorHandler;
 
 		$this->DBClusterTimeout *= 1e6; // convert to milliseconds
 	}
 
 	/**
 	 * Setups load balancer according given configuration
 	 *
 	 * @param Array $config
 	 * @return void
 	 * @access public
 	 */
 	public function setup($config)
 	{
 		$this->servers = Array ();
 
 		$this->servers[0] = Array (
 			'DBHost' => $config['Database']['DBHost'],
 			'DBUser' => $config['Database']['DBUser'],
 			'DBUserPassword' => $config['Database']['DBUserPassword'],
 			'DBName' => $config['Database']['DBName'],
 			'DBLoad' => 0,
 		);
 
 		if ( isset($config['Databases']) ) {
 			$this->servers = array_merge($this->servers, $config['Databases']);
 		}
 
 		foreach ($this->servers as $server_index => $server_setting) {
 			$this->serverLoads[$server_index] = $server_setting['DBLoad'];
 		}
 	}
 
 	/**
 	 * Returns connection index to master database
 	 *
 	 * @return int
 	 * @access protected
 	 */
 	protected function getMasterIndex()
 	{
 		return 0;
 	}
 
 	/**
 	 * Returns connection index to slave database. This takes into account load ratios and lag times.
 	 * Side effect: opens connections to databases
 	 *
 	 * @return int
 	 * @access protected
 	 */
 	protected function getSlaveIndex()
 	{
 		if ( count($this->servers) == 1 || $this->Application->isAdmin ) {
 			// skip the load balancing if there's only one server OR in admin console
 			return 0;
 		}
 		elseif ( $this->slaveIndex !== false ) {
 			// shortcut if generic reader exists already
 			return $this->slaveIndex;
 		}
 
 		$total_elapsed = 0;
 		$non_error_loads = $this->serverLoads;
 		$i = $found = $lagged_slave_mode = false;
 
 		// first try quickly looking through the available servers for a server that meets our criteria
 		do {
 			$current_loads = $non_error_loads;
 			$overloaded_servers = $total_threads_connected = 0;
 
 			while ($current_loads) {
 				if ( $lagged_slave_mode ) {
 					// when all slave servers are too lagged, then ignore lag and pick random server
 					$i = $this->pickRandom($current_loads);
 				}
 				else {
 					$i = $this->getRandomNonLagged($current_loads);
 
 					if ( $i === false && $current_loads )  {
 						// all slaves lagged -> pick random lagged slave then
 						$lagged_slave_mode = true;
 						$i = $this->pickRandom( $current_loads );
 					}
 				}
 
 				if ( $i === false ) {
 					// all slaves are down -> use master as a slave
 					$this->slaveIndex = $this->getMasterIndex();
 
 					return $this->slaveIndex;
 				}
 
 				$conn =& $this->openConnection($i);
 
 				if ( !$conn ) {
 					unset($non_error_loads[$i], $current_loads[$i]);
 					continue;
 				}
 
 				// Perform post-connection backoff
 				$threshold = isset($this->servers[$i]['DBMaxThreads']) ? $this->servers[$i]['DBMaxThreads'] : false;
 				$backoff = $this->postConnectionBackoff($conn, $threshold);
 
 				if ( $backoff ) {
 					// post-connection overload, don't use this server for now
 					$total_threads_connected += $backoff;
 					$overloaded_servers++;
 
 					unset( $current_loads[$i] );
 				}
 				else {
 					// return this server
 					break 2;
 				}
 			}
 
 			// no server found yet
 			$i = false;
 
 			// if all servers were down, quit now
 			if ( !$non_error_loads ) {
 				break;
 			}
 
 			// back off for a while
 			// scale the sleep time by the number of connected threads, to produce a roughly constant global poll rate
 			$avg_threads = $total_threads_connected / $overloaded_servers;
 
 			usleep($this->DBAvgStatusPoll * $avg_threads);
 			$total_elapsed += $this->DBAvgStatusPoll * $avg_threads;
 		} while ( $total_elapsed < $this->DBClusterTimeout );
 
 		if ( $i !== false ) {
 			// slave connection successful
 			if ( $this->slaveIndex <= 0 && $this->serverLoads[$i] > 0 && $i !== false ) {
 				$this->slaveIndex = $i;
 			}
 		}
 
 		return $i;
 	}
 
 	/**
 	 * Returns random non-lagged server
 	 *
 	 * @param Array $loads
 	 * @return int
 	 * @access protected
 	 */
 	protected function getRandomNonLagged($loads)
 	{
 		// unset excessively lagged servers
 		$lags = $this->getLagTimes();
 
 		foreach ($lags as $i => $lag) {
 			if ( $i != 0 && isset($this->servers[$i]['DBMaxLag']) ) {
 				if ( $lag === false ) {
 					unset( $loads[$i] ); // server is not replicating
 				}
 				elseif ( $lag > $this->servers[$i]['DBMaxLag'] ) {
 					unset( $loads[$i] ); // server is excessively lagged
 				}
 			}
 		}
 
 		// find out if all the slaves with non-zero load are lagged
 		if ( !$loads || array_sum($loads) == 0 ) {
 			return false;
 		}
 
 		// return a random representative of the remainder
 		return $this->pickRandom($loads);
 	}
 
 	/**
 	 * Select an element from an array of non-normalised probabilities
 	 *
 	 * @param Array $weights
 	 * @return int
 	 * @access protected
 	 */
 	protected function pickRandom($weights)
 	{
 		if ( !is_array($weights) || !$weights ) {
 			return false;
 		}
 
 		$sum = array_sum($weights);
 
 		if ( $sum == 0 ) {
 			return false;
 		}
 
 		$max = mt_getrandmax();
 		$rand = mt_rand(0, $max) / $max * $sum;
 
 		$index = $sum = 0;
 
 		foreach ($weights as $index => $weight) {
 			$sum += $weight;
 
 			if ( $sum >= $rand ) {
 				break;
 			}
 		}
 
 		return $index;
 	}
 
 	/**
 	 * Get lag time for each server
 	 * Results are cached for a short time in memcached, and indefinitely in the process cache
 	 *
 	 * @return Array
 	 * @access protected
 	 */
 	protected function getLagTimes()
 	{
 		if ( $this->serverLagTimes ) {
 			return $this->serverLagTimes;
 		}
 
 		$expiry = 5;
 		$request_rate = 10;
 
 		$cache_key = 'lag_times:' . $this->servers[0]['DBHost'];
 		$times = $this->Application->getCache($cache_key);
 
 		if ( $times ) {
 			// randomly recache with probability rising over $expiry
 			$elapsed = adodb_mktime() - $times['timestamp'];
 			$chance = max(0, ($expiry - $elapsed) * $request_rate);
 
 			if ( mt_rand(0, $chance) != 0 ) {
 				unset( $times['timestamp'] );
 				$this->serverLagTimes = $times;
 
 				return $times;
 			}
 		}
 
 		// cache key missing or expired
 		$times = Array();
 
 		foreach ($this->servers as $index => $server) {
 			if ($index == 0) {
 				$times[$index] = 0; // master
 			}
 			else {
 				$conn =& $this->openConnection($index);
 
 				if ($conn !== false) {
 					$times[$index] = $conn->getSlaveLag();
 				}
 			}
 		}
 
 		// add a timestamp key so we know when it was cached
 		$times['timestamp'] = adodb_mktime();
 		$this->Application->setCache($cache_key, $times, $expiry);
 
 		// but don't give the timestamp to the caller
 		unset($times['timestamp']);
 		$this->serverLagTimes = $times;
 
 		return $this->serverLagTimes;
 	}
 
 	/**
 	 * Determines whatever server should not be used, even, when connection was made
 	 *
 	 * @param kDBConnection $conn
 	 * @param int $threshold
 	 * @return int
 	 * @access protected
 	 */
 	protected function postConnectionBackoff(&$conn, $threshold)
 	{
 		if ( !$threshold ) {
 			return 0;
 		}
 
 		$status = $conn->getStatus('Thread%');
 
 		return $status['Threads_running'] > $threshold ? $status['Threads_connected'] : 0;
 	}
 
 	/**
 	 * Open a connection to the server given by the specified index
 	 * Index must be an actual index into the array.
 	 * If the server is already open, returns it.
 	 *
 	 * On error, returns false.
 	 *
 	 * @param integer $i Server index
 	 * @return kDBConnection|false
 	 * @access protected
 	 */
 	protected function &openConnection($i)
 	{
 		if ( isset($this->connections[$i]) ) {
 			$conn =& $this->connections[$i];
 		}
 		else {
 			$server = $this->servers[$i];
 			$server['serverIndex'] = $i;
 			$conn =& $this->reallyOpenConnection($server, $i == $this->getMasterIndex());
 
 			if ( $conn->connectionOpened ) {
 				$this->connections[$i] =& $conn;
 				$this->lastUsedIndex = $i;
 			}
 			else {
 				$conn = false;
 			}
 		}
 
 		if ( $this->nextQueryCachable && is_object($conn) ) {
 			$conn->nextQueryCachable = true;
 			$this->nextQueryCachable = false;
 		}
 
 		return $conn;
 	}
 
 	/**
 	 * Really opens a connection.
 	 * Returns a database object whether or not the connection was successful.
 	 *
 	 * @param Array $server
 	 * @param bool $is_master
 	 * @return kDBConnection
 	 */
 	protected function &reallyOpenConnection($server, $is_master)
 	{
-		$db = $this->Application->makeClass( 'kDBConnection', Array ($this->dbType, $this->errorHandler, $server['serverIndex']) );
+		$debug_mode = $this->Application->isDebugMode();
+		$db_class = $debug_mode ? 'kDBConnectionDebug' : 'kDBConnection';
+
+		$db = $this->Application->makeClass($db_class, Array ($this->dbType, $this->errorHandler, $server['serverIndex']));
 		/* @var $db kDBConnection */
 
-		$db->debugMode = $this->Application->isDebugMode();
+		$db->debugMode = $debug_mode;
 		$db->Connect($server['DBHost'], $server['DBUser'], $server['DBUserPassword'], $this->servers[0]['DBName'], true, !$is_master);
 
 		return $db;
 	}
 
 	/**
 	 * Returns first field of first line of recordset if query ok or false otherwise
 	 *
 	 * @param string $sql
 	 * @param int $offset
 	 * @return string
 	 * @access public
 	 */
 	public function GetOne($sql, $offset = 0)
 	{
 		$conn =& $this->chooseConnection($sql);
 
 		return $conn->GetOne($sql, $offset);
 	}
 
 	/**
 	 * Returns first row of recordset if query ok, false otherwise
 	 *
 	 * @param string $sql
 	 * @param int $offset
 	 * @return Array
 	 * @access public
 	 */
 	public function GetRow($sql, $offset = 0)
 	{
 		$conn =& $this->chooseConnection($sql);
 
 		return $conn->GetRow($sql, $offset);
 	}
 
 	/**
 	 * Returns 1st column of recordset as one-dimensional array or false otherwise
 	 * Optional parameter $key_field can be used to set field name to be used as resulting array key
 	 *
 	 * @param string $sql
 	 * @param string $key_field
 	 * @return Array
 	 * @access public
 	 */
 	public function GetCol($sql, $key_field = null)
 	{
 		$conn =& $this->chooseConnection($sql);
 
 		return $conn->GetCol($sql, $key_field);
 	}
 
 	/**
 	 * Queries db with $sql query supplied and returns rows selected if any, false
 	 * otherwise. Optional parameter $key_field allows to set one of the query fields
 	 * value as key in string array.
 	 *
 	 * @param string $sql
 	 * @param string $key_field
 	 * @param bool $no_debug
 	 * @return Array
 	 * @access public
 	 */
 	public function Query($sql, $key_field = null, $no_debug = false)
 	{
 		$conn =& $this->chooseConnection($sql);
 
 		return $conn->Query($sql, $key_field, $no_debug);
 	}
 
 	/**
 	 * Performs sql query, that will change database content
 	 *
 	 * @param string $sql
 	 * @return bool
 	 * @access public
 	 */
 	public function ChangeQuery($sql)
 	{
 		$conn =& $this->chooseConnection($sql);
 
 		return $conn->ChangeQuery($sql);
 	}
 
 	/**
 	 * If it's a string, adds quotes and backslashes (only work since PHP 4.3.0)
 	 * Otherwise returns as-is
 	 *
 	 * @param mixed $string
 	 * @return string
 	 * @access public
 	 */
 	public function qstr($string)
 	{
 		$conn =& $this->openConnection($this->lastUsedIndex);
 
 		return $conn->qstr($string);
 	}
 
 	/**
 	 * Calls "qstr" function for each given array element
 	 *
 	 * @param Array $array
 	 * @param string $function
 	 * @return Array
 	 */
 	public function qstrArray($array, $function = 'qstr')
 	{
 		$conn =& $this->openConnection($this->lastUsedIndex);
 
 		return $conn->qstrArray($array, $function);
 	}
 
 	/**
 	 * Performs insert of given data (useful with small number of queries)
 	 * or stores it to perform multiple insert later (useful with large number of queries)
 	 *
 	 * @param Array $fields_hash
 	 * @param string $table
 	 * @param string $type
 	 * @param bool $insert_now
 	 * @return bool
 	 * @access public
 	 */
 	public function doInsert($fields_hash, $table, $type = 'INSERT', $insert_now = true)
 	{
 		$conn =& $this->openConnection( $this->getMasterIndex() );
 
 		return $conn->doInsert($fields_hash, $table, $type, $insert_now);
 	}
 
 	/**
 	 * Update given field values to given record using $key_clause
 	 *
 	 * @param Array $fields_hash
 	 * @param string $table
 	 * @param string $key_clause
 	 * @return bool
 	 * @access public
 	 */
 	public function doUpdate($fields_hash, $table, $key_clause)
 	{
 		$conn =& $this->openConnection( $this->getMasterIndex() );
 
 		return $conn->doUpdate($fields_hash, $table, $key_clause);
 	}
 
 	/**
 	 * When undefined method is called, then send it directly to last used slave server connection
 	 *
 	 * @param string $name
 	 * @param Array $arguments
 	 * @return mixed
 	 * @access public
 	 */
 	public function __call($name, $arguments)
 	{
 		$conn =& $this->openConnection($this->lastUsedIndex);
 
 		return call_user_func_array( Array (&$conn, $name), $arguments );
     }
 
     /**
      * Returns appropriate connection based on sql
      *
      * @param string $sql
      * @return kDBConnection
 	 * @access protected
      */
     protected function &chooseConnection($sql)
     {
     	if ( $this->nextQueryFromMaster ) {
 			$this->nextQueryFromMaster = false;
 			$index = $this->getMasterIndex();
 		}
 		else {
 			$sid = isset($this->Application->Session) ? $this->Application->GetSID() : '9999999999999999999999';
 
 			if ( preg_match('/(^[ \t\r\n]*(ALTER|CREATE|DROP|RENAME|DELETE|DO|INSERT|LOAD|REPLACE|TRUNCATE|UPDATE))|ses_' . $sid . '/', $sql) ) {
 				$index = $this->getMasterIndex();
 			}
 			else {
 				$index = $this->getSlaveIndex();
 			}
 		}
 
 		$this->lastUsedIndex = $index;
 		$conn =& $this->openConnection($index);
 
 		return $conn;
     }
 }
Index: branches/5.2.x/core/kernel/db/db_connection.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_connection.php	(revision 15238)
+++ branches/5.2.x/core/kernel/db/db_connection.php	(revision 15239)
@@ -1,1106 +1,1463 @@
 <?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!');
 
 	/**
 	 * Multi database connection class
 	 *
 	 */
 	class kDBConnection extends kBase {
 
 		/**
 		 * Current database type
 		 *
 		 * @var string
 		 * @access protected
 		 */
 		protected $dbType = 'mysql';
 
 		/**
 		 * Created connection handle
 		 *
 		 * @var resource
 		 * @access protected
 		 */
 		protected $connectionID = null;
 
 		/**
 		 * Remembers, that database connection was opened successfully
 		 *
 		 * @var bool
 		 * @access public
 		 */
 		public $connectionOpened = false;
 
 		/**
 		 * Connection parameters, that were used
 		 *
 		 * @var Array
 		 * @access protected
 		 */
 		protected $connectionParams = Array ('host' => '', 'user' => '', 'pass' => '', 'db' => '');
 
 		/**
 		 * Index of database server
 		 *
 		 * @var int
 		 * @access protected
 		 */
 		protected $serverIndex = 0;
 
 		/**
 		 * Handle of currently processed recordset
 		 *
 		 * @var resource
 		 * @access protected
 		 */
 		protected $queryID = null;
 
 		/**
 		 * DB type specific function mappings
 		 *
 		 * @var Array
 		 * @access protected
 		 */
 		protected $metaFunctions = Array ();
 
 		/**
 		 * Function to handle sql errors
 		 *
 		 * @var Array|string
 		 * @access public
 		 */
 		public $errorHandler = '';
 
 		/**
 		 * Error code
 		 *
 		 * @var int
 		 * @access protected
 		 */
 		protected $errorCode = 0;
 
 		/**
 		 * Error message
 		 *
 		 * @var string
 		 * @access protected
 		 */
 		protected $errorMessage = '';
 
 		/**
 		 * Defines if database connection
 		 * operations should generate debug
 		 * information
 		 *
 		 * @var bool
 		 * @access public
 		 */
 		public $debugMode = false;
 
 		/**
 		 * Save query execution statistics
 		 *
 		 * @var bool
 		 * @access protected
 		 */
 		protected $_captureStatistics = false;
 
 		/**
 		 * Last query to database
 		 *
 		 * @var string
 		 * @access public
 		 */
 		public $lastQuery = '';
 
 		/**
 		 * Total processed queries count
 		 *
 		 * @var int
 		 * @access protected
 		 */
 		protected $_queryCount = 0;
 
 		/**
 		 * Total time, used for serving queries
 		 *
 		 * @var Array
 		 * @access protected
 		 */
 		protected $_queryTime = 0;
 
 		/**
 		 * Indicates, that next database query could be cached, when memory caching is enabled
 		 *
 		 * @var bool
 		 * @access public
 		 */
 		public $nextQueryCachable = false;
 
 		/**
 		 * For backwards compatibility with kDBLoadBalancer class
 		 *
 		 * @var bool
 		 * @access public
 		 */
 		public $nextQueryFromMaster = false;
 
 		/**
 		 * Initializes connection class with
 		 * db type to used in future
 		 *
 		 * @param string $dbType
 		 * @param string $errorHandler
 		 * @param int $server_index
 		 * @access public
 		 */
 		public function __construct($dbType, $errorHandler = '', $server_index = 0)
 		{
 			if ( class_exists('kApplication') ) {
 				// prevents "Fatal Error" on 2nd installation step (when database is empty)
 				parent::__construct();
 			}
 
 			$this->dbType = $dbType;
 			$this->serverIndex = $server_index;
 
 //			$this->initMetaFunctions();
 			if (!$errorHandler) {
 				$this->errorHandler = Array(&$this, 'handleError');
 			}
 			else {
 				$this->errorHandler = $errorHandler;
 			}
 
 			$this->_captureStatistics = defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !(defined('ADMIN') && ADMIN);
 		}
 
 		/**
 		 * Set's custom error
 		 *
 		 * @param int $code
 		 * @param string $msg
 		 * @access protected
 		 */
 		protected function setError($code, $msg)
 		{
 			$this->errorCode = $code;
 			$this->errorMessage = $msg;
 		}
 
 		/**
 		 * Checks if previous query execution
 		 * raised an error.
 		 *
 		 * @return bool
 		 * @access public
 		 */
 		public function hasError()
 		{
 			return $this->errorCode != 0;
 		}
 
 		/**
 		 * Caches function specific to requested
 		 * db type
 		 *
 		 * @access protected
 		 */
 		protected function initMetaFunctions()
 		{
 			$ret = Array ();
 
 			switch ( $this->dbType ) {
 				case 'mysql':
 					$ret = Array (); // only define functions, that name differs from "dbType_<meta_name>"
 
 					break;
 			}
 
 			$this->metaFunctions = $ret;
 		}
 
 		/**
 		 * Gets function for specific db type
 		 * based on it's meta name
 		 *
 		 * @param string $name
 		 * @return string
 		 * @access protected
 		 */
 		protected function getMetaFunction($name)
 		{
 			/*if ( !isset($this->metaFunctions[$name]) ) {
 				$this->metaFunctions[$name] = $name;
 			}*/
 
 			return $this->dbType . '_' . $name;
 		}
 
 
 		/**
 		 * Try to connect to database server
 		 * using specified parameters and set
 		 * database to $db if connection made
 		 *
 		 * @param string $host
 		 * @param string $user
 		 * @param string $pass
 		 * @param string $db
 		 * @param bool $force_new
 		 * @param bool $retry
 		 * @return bool
 		 * @access public
 		 */
 		public function Connect($host, $user, $pass, $db, $force_new = false, $retry = false)
 		{
 			$this->connectionParams = Array ('host' => $host, 'user' => $user, 'pass' => $pass, 'db' => $db);
 
 			$func = $this->getMetaFunction('connect');
 			$this->connectionID = $func($host, $user, $pass, $force_new);
 
 			if ($this->connectionID) {
 				if (defined('DBG_SQL_MODE')) {
 					$this->Query('SET sql_mode = \''.DBG_SQL_MODE.'\'');
 				}
 
 				if (defined('SQL_COLLATION') && defined('SQL_CHARSET')) {
 					$this->Query('SET NAMES \''.SQL_CHARSET.'\' COLLATE \''.SQL_COLLATION.'\'');
 				}
 
 				$this->setError(0, ''); // reset error
 				$this->setDB($db);
 			}
 
 			// process error (fatal in most cases)
 			$func = $this->getMetaFunction('errno');
 			$this->errorCode = $this->connectionID ? $func($this->connectionID) : $func();
 
 			if ( is_resource($this->connectionID) && !$this->hasError() ) {
 				$this->connectionOpened = true;
 
 				return true;
 			}
 
 			$func = $this->getMetaFunction('error');
 			$this->errorMessage = $this->connectionID ? $func($this->connectionID) : $func();
 			$error_msg = 'Database connection failed, please check your connection settings.<br/>Error (' . $this->errorCode . '): ' . $this->errorMessage;
 
 			if ( (defined('IS_INSTALL') && IS_INSTALL) || $retry ) {
 				trigger_error($error_msg, E_USER_WARNING);
 			}
 			else {
 				$this->Application->redirectToMaintenance();
 
 				throw new Exception($error_msg);
 			}
 
 			$this->connectionOpened = false;
 
 			return false;
 		}
 
 		/**
 		 * Setups the connection according given configuration
 		 *
 		 * @param Array $config
 		 * @return bool
 		 * @access public
 		 */
 		public function setup($config)
 		{
 			if ( is_object($this->Application) ) {
 				$this->debugMode = $this->Application->isDebugMode();
 			}
 
 			return $this->Connect(
 				$config['Database']['DBHost'],
 				$config['Database']['DBUser'],
 				$config['Database']['DBUserPassword'],
 				$config['Database']['DBName']
 			);
 		}
 
 		/**
 		 * Performs 3 reconnect attempts in case if connection to a DB was lost in the middle of script run (e.g. server restart)
 		 *
 		 * @param bool $force_new
 		 * @return bool
 		 * @access protected
 		 */
 		protected function ReConnect($force_new = false)
 		{
 			$retry_count = 0;
 			$connected = false;
 
 			$func = $this->getMetaFunction('close');
 			$func($this->connectionID);
 
 			while ( $retry_count < 3 ) {
 				sleep(5); // wait 5 seconds before each reconnect attempt
 
 				$connected = $this->Connect(
 					$this->connectionParams['host'],
 					$this->connectionParams['user'],
 					$this->connectionParams['pass'],
 					$this->connectionParams['db'],
 					$force_new, true
 				);
 
 				if ( $connected ) {
 					break;
 				}
 
 				$retry_count++;
 			}
 
 			return $connected;
 		}
 
 		/**
 		 * Shows error message from previous operation
 		 * if it failed
 		 *
 		 * @param string $sql
 		 * @param string $key_field
 		 * @param bool $no_debug
 		 * @return bool
 		 * @access protected
 		 */
 		protected function showError($sql = '', $key_field = null, $no_debug = false)
 		{
 			static $retry_count = 0;
 
 			$func = $this->getMetaFunction('errno');
 
 			if (!$this->connectionID) {
 				// no connection while doing mysql_query
 				$this->errorCode = $func();
 
 				if ( $this->hasError() ) {
 					$func = $this->getMetaFunction('error');
 					$this->errorMessage = $func();
 
 					$ret = $this->callErrorHandler($sql);
 
 					if (!$ret) {
 						exit;
 					}
 				}
 
 				return false;
 			}
 
 			// checking if there was an error during last mysql_query
 			$this->errorCode = $func($this->connectionID);
 
 			if ( $this->hasError() ) {
 				$func = $this->getMetaFunction('error');
 				$this->errorMessage = $func($this->connectionID);
 
 				$ret = $this->callErrorHandler($sql);
 
 				if ( ($this->errorCode == 2006 || $this->errorCode == 2013) && ($retry_count < 3) ) {
 					// #2006 - MySQL server has gone away
 					// #2013 - Lost connection to MySQL server during query
 					$retry_count++;
 
 					if ( $this->ReConnect() ) {
 						return $this->Query($sql, $key_field, $no_debug);
 					}
 				}
 
 				if (!$ret) {
 					exit;
 				}
 			}
 			else {
 				$retry_count = 0;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Sends db error to a predefined error handler
 		 *
 		 * @param $sql
 		 * @return bool
 		 * @access protected
 		 */
 		protected function callErrorHandler($sql)
 		{
 			if (is_array($this->errorHandler)) {
 				$func = $this->errorHandler[1];
 				$ret = $this->errorHandler[0]->$func($this->errorCode, $this->errorMessage, $sql);
 			}
 			else {
 				$func = $this->errorHandler;
 				$ret = $func($this->errorCode, $this->errorMessage, $sql);
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Default error handler for sql errors
 		 *
 		 * @param int $code
 		 * @param string $msg
 		 * @param string $sql
 		 * @return bool
 		 * @access public
 		 */
 		public function handleError($code, $msg, $sql)
 		{
 			echo '<strong>Processing SQL</strong>: ' . $sql . '<br/>';
 			echo '<strong>Error (' . $code . '):</strong> ' . $msg . '<br/>';
 
 			return false;
 		}
 
 		/**
 		 * Set's database name for connection
 		 * to $new_name
 		 *
 		 * @param string $new_name
 		 * @return bool
 		 * @access protected
 		 */
 		protected function setDB($new_name)
 		{
 			if (!$this->connectionID) return false;
 			$func = $this->getMetaFunction('select_db');
 			return $func($new_name, $this->connectionID);
 		}
 
 		/**
 		 * Returns first field of first line
 		 * of recordset if query ok or false
 		 * otherwise
 		 *
 		 * @param string $sql
 		 * @param int $offset
 		 * @return string
 		 * @access public
 		 */
 		public function GetOne($sql, $offset = 0)
 		{
 			$row = $this->GetRow($sql, $offset);
 
 			if ( !$row ) {
 				return false;
 			}
 
 			return array_shift($row);
 		}
 
 		/**
 		 * Returns first row of recordset
 		 * if query ok, false otherwise
 		 *
 		 * @param string $sql
 		 * @param int $offset
 		 * @return Array
 		 * @access public
 		 */
 		public function GetRow($sql, $offset = 0)
 		{
 			$sql .= ' ' . $this->getLimitClause($offset, 1);
 			$ret = $this->Query($sql);
 
 			if ( !$ret ) {
 				return false;
 			}
 
 			return array_shift($ret);
 		}
 
 		/**
 		 * Returns 1st column of recordset as
 		 * one-dimensional array or false otherwise
 		 * Optional parameter $key_field can be used
 		 * to set field name to be used as resulting
 		 * array key
 		 *
 		 * @param string $sql
 		 * @param string $key_field
 		 * @return Array
 		 * @access public
 		 */
 		public function GetCol($sql, $key_field = null)
 		{
 			$rows = $this->Query($sql);
 			if ( !$rows ) {
 				return $rows;
 			}
 
 			$i = 0;
 			$row_count = count($rows);
 			$ret = Array ();
 
 			if ( isset($key_field) ) {
 				while ( $i < $row_count ) {
 					$ret[$rows[$i][$key_field]] = array_shift($rows[$i]);
 					$i++;
 				}
 			}
 			else {
 				while ( $i < $row_count ) {
 					$ret[] = array_shift($rows[$i]);
 					$i++;
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Queries db with $sql query supplied
 		 * and returns rows selected if any, false
 		 * otherwise. Optional parameter $key_field
 		 * allows to set one of the query fields
 		 * value as key in string array.
 		 *
 		 * @param string $sql
 		 * @param string $key_field
 		 * @param bool $no_debug
 		 * @return Array
 		 * @access public
 		 */
 		public function Query($sql, $key_field = null, $no_debug = false)
 		{
+			$this->_queryCount++;
 			$this->lastQuery = $sql;
-			if ( !$no_debug ) {
-				$this->_queryCount++;
-			}
 
-			if ( $this->debugMode && !$no_debug ) {
-				return $this->debugQuery($sql, $key_field);
-			}
 			$query_func = $this->getMetaFunction('query');
 
 			// set 1st checkpoint: begin
-			if ( $this->_captureStatistics ) {
-				$start_time = microtime(true);
-			}
+			$start_time = $this->_captureStatistics ? microtime(true) : 0;
 			// set 1st checkpoint: end
 
 			$this->setError(0, ''); // reset error
 			$this->queryID = $query_func($sql, $this->connectionID);
+
 			if ( is_resource($this->queryID) ) {
 				$ret = Array ();
 				$fetch_func = $this->getMetaFunction('fetch_assoc');
+
 				if ( isset($key_field) ) {
-					while ( ($row = $fetch_func($this->queryID)) ) {
+					while (($row = $fetch_func($this->queryID))) {
 						$ret[$row[$key_field]] = $row;
 					}
 				}
 				else {
-					while ( ($row = $fetch_func($this->queryID)) ) {
+					while (($row = $fetch_func($this->queryID))) {
 						$ret[] = $row;
 					}
 				}
 
 				// set 2nd checkpoint: begin
 				if ( $this->_captureStatistics ) {
 					$query_time = microtime(true) - $start_time;
-					if ( $query_time > DBG_MAX_SQL_TIME && !$no_debug ) {
+
+					if ( $query_time > DBG_MAX_SQL_TIME ) {
 						$this->Application->logSlowQuery($sql, $query_time);
 					}
+
 					$this->_queryTime += $query_time;
 				}
 				// set 2nd checkpoint: end
 
 				$this->Destroy();
+
 				return $ret;
 			}
 			else {
 				// set 2nd checkpoint: begin
 				if ( $this->_captureStatistics ) {
 					$this->_queryTime += microtime(true) - $start_time;
 				}
 				// set 2nd checkpoint: end
 			}
 
 			return $this->showError($sql, $key_field, $no_debug);
 		}
 
 		/**
-		 * Retrieves data from database and return resource id on success or false otherwise
+		 * Returns iterator to a recordset, produced from running $sql query  Queries db with $sql query supplied and returns kMySQLQuery iterator
+		 * or false in case of error. Optional parameter $key_field allows to
+		 * set one of the query fields value as key in string array.
 		 *
-		 * @param string $select_sql
-		 * @return resource
+		 * @param string $sql
+		 * @param string $key_field
+		 * @param bool $no_debug
+		 * @param string $iterator_class
+		 * @return kMySQLQuery|bool
 		 * @access public
-		 * @see kDBConnection::GetNextRow
 		 */
-		public function QueryRaw($select_sql)
+		public function GetIterator($sql, $key_field = null, $no_debug = false, $iterator_class = 'kMySQLQuery')
 		{
-			$this->lastQuery = $select_sql;
 			$this->_queryCount++;
+			$this->lastQuery = $sql;
 
 			$query_func = $this->getMetaFunction('query');
 
 			// set 1st checkpoint: begin
-			if ($this->_captureStatistics) {
-				$start_time = microtime(true);
-			}
+			$start_time = $this->_captureStatistics ? microtime(true) : 0;
 			// set 1st checkpoint: end
 
 			$this->setError(0, ''); // reset error
-			$resource = $query_func($select_sql, $this->connectionID);
-
-			// set 2nd checkpoint: begin
-			if ($this->_captureStatistics) {
-				$query_time = microtime(true) - $start_time;
-				if ($query_time > DBG_MAX_SQL_TIME) {
-					$this->Application->logSlowQuery($select_sql, $query_time);
-				}
-				$this->_queryTime += $query_time;
-			}
-			// set 2nd checkpoint: end
+			$this->queryID = $query_func($sql, $this->connectionID);
 
-			if ( is_resource($resource) ) {
-				return $resource;
-			}
+			if ( is_resource($this->queryID) ) {
+				$ret = new $iterator_class($this->queryID, $key_field);
+				/* @var $ret kMySQLQuery */
 
-			return $this->showError($select_sql);
-		}
+				// set 2nd checkpoint: begin
+				if ( $this->_captureStatistics ) {
+					$query_time = microtime(true) - $start_time;
 
-		/**
-		 * Returns row count in recordset
-		 *
-		 * @param resource $resource
-		 * @return int
-		 * @access public
-		 */
-		public function RowCount($resource)
-		{
-			$count_func = $this->getMetaFunction('num_rows');
+					if ( $query_time > DBG_MAX_SQL_TIME ) {
+						$this->Application->logSlowQuery($sql, $query_time);
+					}
 
-			return $count_func($resource);
-		}
+					$this->_queryTime += $query_time;
+				}
+				// set 2nd checkpoint: end
 
-		/**
-		 * Returns next available row from recordset
-		 *
-		 * @param resource $resource
-		 * @param string $fetch_type
-		 * @return Array
-		 * @access public
-		 * @see kDBConnection::QueryRaw
-		 */
-		public function GetNextRow($resource, $fetch_type = 'assoc')
-		{
-			$fetch_func = $this->getMetaFunction('fetch_' . $fetch_type);
+				return $ret;
+			}
+			else {
+				// set 2nd checkpoint: begin
+				if ( $this->_captureStatistics ) {
+					$this->_queryTime += microtime(true) - $start_time;
+				}
+				// set 2nd checkpoint: end
+			}
 
-			return $fetch_func($resource);
+			return $this->showError($sql, $key_field, $no_debug);
 		}
 
 		/**
 		 * Free memory used to hold recordset handle
 		 *
-		 * @param resource $resource
 		 * @access public
-		 * @see kDBConnection::QueryRaw
 		 */
-		public function Destroy($resource = null)
+		public function Destroy()
 		{
-			if ( !isset($resource) ) {
-				$resource = $this->queryID;
-			}
+			$free_func = $this->getMetaFunction('free_result');
+			$free_func($this->queryID);
 
-			if ( $resource ) {
-				$free_func = $this->getMetaFunction('free_result');
-				$free_func($resource);
-
-				$this->queryID = null;
-			}
+			unset($this->queryID);
 		}
 
 		/**
 		 * Performs sql query, that will change database content
 		 *
 		 * @param string $sql
 		 * @return bool
 		 * @access public
 		 */
 		public function ChangeQuery($sql)
 		{
 			$this->Query($sql);
-			return $this->errorCode == 0 ? true : false;
-		}
 
-		/**
-		 * Queries db with $sql query supplied
-		 * and returns rows selected if any, false
-		 * otherwise. Optional parameter $key_field
-		 * allows to set one of the query fields
-		 * value as key in string array.
-		 *
-		 * Each database query will be profiled into Debugger.
-		 *
-		 * @param string $sql
-		 * @param string $key_field
-		 * @return Array
-		 * @access public
-		 */
-		protected function debugQuery($sql, $key_field = null)
-		{
-			global $debugger;
-			$query_func = $this->getMetaFunction('query');
-
-			// set 1st checkpoint: begin
-			$profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE;
-			if ( $profileSQLs ) {
-				$queryID = $debugger->generateID();
-				$debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql));
-			}
-			// set 1st checkpoint: end
-
-			$this->setError(0, ''); // reset error
-			$this->queryID = $query_func($sql, $this->connectionID);
-
-			if ( is_resource($this->queryID) ) {
-				$ret = Array ();
-				$fetch_func = $this->getMetaFunction('fetch_assoc');
-				if ( isset($key_field) ) {
-					while ( ($row = $fetch_func($this->queryID)) ) {
-						$ret[$row[$key_field]] = $row;
-					}
-				}
-				else {
-					while ( ($row = $fetch_func($this->queryID)) ) {
-						$ret[] = $row;
-					}
-				}
-
-				// set 2nd checkpoint: begin
-				if ( $profileSQLs ) {
-					$first_cell = count($ret) == 1 && count(current($ret)) == 1 ? current(current($ret)) : null;
-
-					if ( strlen($first_cell) > 200 ) {
-						$first_cell = substr($first_cell, 0, 50) . ' ...';
-					}
-
-					$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
-					$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
-					$this->nextQueryCachable = false;
-				}
-				// set 2nd checkpoint: end
+			return !$this->hasError();
+		}
 
-				$this->Destroy();
-				return $ret;
-			}
-			else {
-				// set 2nd checkpoint: begin
-				if ( $profileSQLs ) {
-					$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
-					$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
-					$this->nextQueryCachable = false;
-				}
-				// set 2nd checkpoint: end
-			}
 
-			return $this->showError($sql, $key_field);
-		}
 
 		/**
 		 * Returns auto increment field value from
 		 * insert like operation if any, zero otherwise
 		 *
 		 * @return int
 		 * @access public
 		 */
 		public function getInsertID()
 		{
 			$func = $this->getMetaFunction('insert_id');
 			return $func($this->connectionID);
 		}
 
 		/**
 		 * Returns row count affected by last query
 		 *
 		 * @return int
 		 * @access public
 		 */
 		public function getAffectedRows()
 		{
 			$func = $this->getMetaFunction('affected_rows');
 			return $func($this->connectionID);
 		}
 
 		/**
 		 * Returns LIMIT sql clause part for specific db
 		 *
 		 * @param int $offset
 		 * @param int $rows
 		 * @return string
 		 * @access public
 		 */
 		public function getLimitClause($offset, $rows)
 		{
 			if ( !($rows > 0) ) {
 				return '';
 			}
 
 			switch ( $this->dbType ) {
 				default:
 					return 'LIMIT ' . $offset . ',' . $rows;
 					break;
 			}
 		}
 
 		/**
 		 * If it's a string, adds quotes and backslashes (only work since PHP 4.3.0)
 		 * Otherwise returns as-is
 		 *
 		 * @param mixed $string
 		 * @return string
 		 * @access public
 		 */
 		public function qstr($string)
 		{
 			if ( is_null($string) ) {
 				return 'NULL';
 			}
 
 			# This will also quote numeric values. This should be harmless,
 			# and protects against weird problems that occur when they really
 			# _are_ strings such as article titles and string->number->string
 			# conversion is not 1:1.
 			return "'" . mysql_real_escape_string($string, $this->connectionID) . "'";
 		}
 
 		/**
 		 * Calls "qstr" function for each given array element
 		 *
 		 * @param Array $array
 		 * @param string $function
 		 * @return Array
 		 */
 		public function qstrArray($array, $function = 'qstr')
 		{
 			return array_map(Array (&$this, $function), $array);
 		}
 
 		/**
 		 * Escapes strings (only work since PHP 4.3.0)
 		 *
 		 * @param mixed $string
 		 * @return string
 		 * @access public
 		 */
 		public function escape($string)
 		{
 			if ( is_null($string) ) {
 				return 'NULL';
 			}
 
 			$string = mysql_real_escape_string($string, $this->connectionID);
 
 			// prevent double-escaping of MySQL wildcard symbols ("%" and  "_") in case if they were already escaped
 			return str_replace(Array ('\\\\%', '\\\\_'), Array ('\\%', '\\_'), $string);
 		}
 
 		/**
 		 * Returns last error code occurred
 		 *
 		 * @return int
 		 * @access public
 		 */
 		public function getErrorCode()
 		{
 			return $this->errorCode;
 		}
 
 		/**
 		 * Returns last error message
 		 *
 		 * @return string
 		 * @access public
 		 */
 		public function getErrorMsg()
 		{
 			return $this->errorMessage;
 		}
 
 		/**
 		 * Performs insert of given data (useful with small number of queries)
 		 * or stores it to perform multiple insert later (useful with large number of queries)
 		 *
 		 * @param Array $fields_hash
 		 * @param string $table
 		 * @param string $type
 		 * @param bool $insert_now
 		 * @return bool
 		 * @access public
 		 */
 		public function doInsert($fields_hash, $table, $type = 'INSERT', $insert_now = true)
 		{
 			static $value_sqls = Array ();
 
 			if ($insert_now) {
 				$fields_sql = '`' . implode('`,`', array_keys($fields_hash)) . '`';
 			}
 
 			$values_sql = '';
 			foreach ($fields_hash as $field_name => $field_value) {
 				$values_sql .= $this->qstr($field_value) . ',';
 			}
 
 			// don't use preg here, as it may fail when string is too long
 			$value_sqls[] = rtrim($values_sql, ',');
 
 			$insert_result = true;
 			if ($insert_now) {
 				$insert_count = count($value_sqls);
 				if (($insert_count > 1) && ($value_sqls[$insert_count - 1] == $value_sqls[$insert_count - 2])) {
 					// last two records are the same
 					array_pop($value_sqls);
 				}
 
 				$sql = strtoupper($type) . ' INTO `' . $table . '` (' . $fields_sql . ') VALUES (' . implode('),(', $value_sqls) . ')';
 				$value_sqls = Array (); // reset before query to prevent repeated call from error handler to insert 2 records instead of 1
 
 				$insert_result = $this->ChangeQuery($sql);
 			}
 
 			return $insert_result;
 		}
 
 		/**
 		 * Update given field values to given record using $key_clause
 		 *
 		 * @param Array $fields_hash
 		 * @param string $table
 		 * @param string $key_clause
 		 * @return bool
 		 * @access public
 		 */
 		public function doUpdate($fields_hash, $table, $key_clause)
 		{
 			if (!$fields_hash) return true;
 
 			$fields_sql = '';
 			foreach ($fields_hash as $field_name => $field_value) {
 				$fields_sql .= '`'.$field_name.'` = ' . $this->qstr($field_value) . ',';
 			}
 
 			// don't use preg here, as it may fail when string is too long
 			$fields_sql = rtrim($fields_sql, ',');
 
 			$sql = 'UPDATE `'.$table.'` SET '.$fields_sql.' WHERE '.$key_clause;
 
 			return $this->ChangeQuery($sql);
 		}
 
 		/**
 		 * Allows to detect table's presence in database
 		 *
 		 * @param string $table_name
 		 * @param bool $force
 		 * @return bool
 		 * @access public
 		 */
 		public function TableFound($table_name, $force = false)
 		{
 			static $table_found = false;
 
 			if ( $table_found === false ) {
 				$table_found = array_flip($this->GetCol('SHOW TABLES'));
 			}
 
 			if ( !preg_match('/^' . preg_quote(TABLE_PREFIX, '/') . '(.*)/', $table_name) ) {
 				$table_name = TABLE_PREFIX . $table_name;
 			}
 
 			if ( $force ) {
 				if ( $this->Query('SHOW TABLES LIKE ' . $this->qstr($table_name)) ) {
 					$table_found[$table_name] = 1;
 				}
 				else {
 					unset($table_found[$table_name]);
 				}
 			}
 
 			return isset($table_found[$table_name]);
 		}
 
 		/**
 		 * Returns query processing statistics
 		 *
 		 * @return Array
 		 * @access public
 		 */
 		public function getQueryStatistics()
 		{
 			return Array ('time' => $this->_queryTime, 'count' => $this->_queryCount);
 		}
 
 		/**
 		 * Get status information from SHOW STATUS in an associative array
 		 *
 		 * @param string $which
 		 * @return Array
 		 * @access public
 		 */
 		public function getStatus($which = '%')
 		{
 			$status = Array ();
 			$records = $this->Query('SHOW STATUS LIKE "' . $which . '"');
 
 			foreach ($records as $record) {
 				$status[ $record['Variable_name'] ] = $record['Value'];
 			}
 
 			return $status;
 		}
 
 		/**
 		 * Get slave replication lag. It will only work if the DB user has the PROCESS privilege.
 		 *
 		 * @return int
 		 * @access public
 		 */
 		public function getSlaveLag()
 		{
 			// don't use kDBConnection::Query method, since it will create an array of all server processes
-			$rs = $this->QueryRaw('SHOW PROCESSLIST');
+			$processes = $this->GetIterator('SHOW PROCESSLIST');
 
 			$skip_states = Array (
 				'Waiting for master to send event',
 				'Connecting to master',
 				'Queueing master event to the relay log',
 				'Waiting for master update',
 				'Requesting binlog dump',
 			);
 
 			// find slave SQL thread
-			while ( $row = $this->GetNextRow($rs, 'array') ) {
-				if ( $row['User'] == 'system user' &&  !in_array($row['State'], $skip_states) ) {
+			foreach ($processes as $process) {
+				if ( $process['User'] == 'system user' &&  !in_array($process['State'], $skip_states) ) {
 					// this is it, return the time (except -ve)
-					$this->Destroy($rs);
 
-					return $row['Time'] > 0x7fffffff ? false : $row['Time'];
+					return $process['Time'] > 0x7fffffff ? false : $process['Time'];
 				}
 			}
 
-			$this->Destroy($rs);
-
 			return false;
 		}
-	}
\ No newline at end of file
+	}
+
+
+class kDBConnectionDebug extends kDBConnection {
+
+	protected $_profileSQLs = false;
+
+	/**
+	 * Initializes connection class with
+	 * db type to used in future
+	 *
+	 * @param string $dbType
+	 * @param string $errorHandler
+	 * @param int $server_index
+	 * @access public
+	 */
+	public function __construct($dbType, $errorHandler = '', $server_index = 0)
+	{
+		parent::__construct($dbType, $errorHandler, $server_index);
+
+		$this->_profileSQLs = defined('DBG_SQL_PROFILE') && DBG_SQL_PROFILE;
+	}
+
+	/**
+	 * Queries db with $sql query supplied
+	 * and returns rows selected if any, false
+	 * otherwise. Optional parameter $key_field
+	 * allows to set one of the query fields
+	 * value as key in string array.
+	 *
+	 * @param string $sql
+	 * @param string $key_field
+	 * @param bool $no_debug
+	 * @return Array
+	 * @access public
+	 */
+	public function Query($sql, $key_field = null, $no_debug = false)
+	{
+		if ( $no_debug ) {
+			return parent::Query($sql, $key_field, $no_debug);
+		}
+
+		global $debugger;
+
+		$this->_queryCount++;
+		$this->lastQuery = $sql;
+
+		$query_func = $this->getMetaFunction('query');
+
+		// set 1st checkpoint: begin
+		if ( $this->_profileSQLs ) {
+			$queryID = $debugger->generateID();
+			$debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql));
+		}
+		// set 1st checkpoint: end
+
+		$this->setError(0, ''); // reset error
+		$this->queryID = $query_func($sql, $this->connectionID);
+
+		if ( is_resource($this->queryID) ) {
+			$ret = Array ();
+			$fetch_func = $this->getMetaFunction('fetch_assoc');
+
+			if ( isset($key_field) ) {
+				while (($row = $fetch_func($this->queryID))) {
+					$ret[$row[$key_field]] = $row;
+				}
+			}
+			else {
+				while (($row = $fetch_func($this->queryID))) {
+					$ret[] = $row;
+				}
+			}
+
+			// set 2nd checkpoint: begin
+			if ( $this->_profileSQLs ) {
+				$first_cell = count($ret) == 1 && count(current($ret)) == 1 ? current(current($ret)) : null;
+
+				if ( strlen($first_cell) > 200 ) {
+					$first_cell = substr($first_cell, 0, 50) . ' ...';
+				}
+
+				$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
+				$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
+				$this->nextQueryCachable = false;
+			}
+			// set 2nd checkpoint: end
+
+			$this->Destroy();
+
+			return $ret;
+		}
+		else {
+			// set 2nd checkpoint: begin
+			if ( $this->_profileSQLs ) {
+				$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
+				$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
+				$this->nextQueryCachable = false;
+			}
+			// set 2nd checkpoint: end
+		}
+
+		return $this->showError($sql, $key_field);
+	}
+
+	/**
+	 * Queries db with $sql query supplied and returns kMySQLQuery iterator
+	 * or false in case of error. Optional parameter $key_field allows to
+	 * set one of the query fields value as key in string array.
+	 *
+	 * @param string $sql
+	 * @param string $key_field
+	 * @param bool $no_debug
+	 * @param string $iterator_class
+	 * @return kMySQLQuery|bool
+	 * @access public
+	 */
+	public function GetIterator($sql, $key_field = null, $no_debug = false, $iterator_class = 'kMySQLQuery')
+	{
+		if ( $no_debug ) {
+			return parent::Query($sql, $key_field, $no_debug, $iterator_class);
+		}
+
+		global $debugger;
+
+		$this->_queryCount++;
+		$this->lastQuery = $sql;
+
+		$query_func = $this->getMetaFunction('query');
+
+		// set 1st checkpoint: begin
+		if ( $this->_profileSQLs ) {
+			$queryID = $debugger->generateID();
+			$debugger->profileStart('sql_' . $queryID, $debugger->formatSQL($sql));
+		}
+		// set 1st checkpoint: end
+
+		$this->setError(0, ''); // reset error
+		$this->queryID = $query_func($sql, $this->connectionID);
+
+		if ( is_resource($this->queryID) ) {
+			$ret = new $iterator_class($this->queryID, $key_field);
+			/* @var $ret kMySQLQuery */
+
+			// set 2nd checkpoint: begin
+			if ( $this->_profileSQLs ) {
+				$first_cell = count($ret) == 1 && $ret->fieldCount() == 1 ? current($ret->current()) : null;
+
+				if ( strlen($first_cell) > 200 ) {
+					$first_cell = substr($first_cell, 0, 50) . ' ...';
+				}
+
+				$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), $first_cell, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
+				$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
+				$this->nextQueryCachable = false;
+			}
+			// set 2nd checkpoint: end
+
+			return $ret;
+		}
+		else {
+			// set 2nd checkpoint: begin
+			if ( $this->_profileSQLs ) {
+				$debugger->profileFinish('sql_' . $queryID, null, null, $this->getAffectedRows(), null, $this->_queryCount, $this->nextQueryCachable, $this->serverIndex);
+				$debugger->profilerAddTotal('sql', 'sql_' . $queryID);
+				$this->nextQueryCachable = false;
+			}
+			// set 2nd checkpoint: end
+		}
+
+		return $this->showError($sql, $key_field);
+	}
+}
+
+
+class kMySQLQuery implements Iterator, Countable, SeekableIterator {
+
+	/**
+	 * Current index in recordset
+	 *
+	 * @var int
+	 * @access protected
+	 */
+	protected $position = -1;
+
+	/**
+	 * Query resource
+	 *
+	 * @var resource
+	 * @access protected
+	 */
+	protected $result;
+
+	/**
+	 * Field to act as key in a resulting array
+	 *
+	 * @var string
+	 * @access protected
+	 */
+	protected $keyField = null;
+
+	/**
+	 * Data in current row of recordset
+	 *
+	 * @var Array
+	 * @access protected
+	 */
+	protected $rowData = Array ();
+
+	/**
+	 * Row count in a result
+	 *
+	 * @var int
+	 * @access protected
+	 */
+	protected $rowCount = 0;
+
+	/**
+	 * Creates new instance of a class
+	 *
+	 * @param resource $result
+	 * @param null|string $key_field
+	 */
+	public function __construct($result, $key_field = null)
+	{
+		$this->result = $result;
+		$this->keyField = $key_field;
+
+		$this->rowCount = mysql_num_rows($this->result);
+		$this->rewind();
+	}
+
+	/**
+	 * Moves recordset pointer to first element
+	 *
+	 * @return void
+	 * @access public
+	 * @implements Iterator::rewind
+	 */
+	public function rewind()
+	{
+		$this->seek(0);
+	}
+
+	/**
+	 * Returns value at current position
+	 *
+	 * @return mixed
+	 * @access public
+	 * @implements Iterator::current
+	 */
+	function current()
+	{
+		return $this->rowData;
+	}
+
+	/**
+	 * Returns key at current position
+	 *
+	 * @return mixed
+	 * @access public
+	 * @implements Iterator::key
+	 */
+	function key()
+	{
+		return $this->keyField ? $this->rowData[$this->keyField] : $this->position;
+	}
+
+	/**
+	 * Moves recordset pointer to next position
+	 *
+	 * @return void
+	 * @access public
+	 * @implements Iterator::next
+	 */
+	function next()
+	{
+		$this->seek($this->position + 1);
+	}
+
+	/**
+	 * Detects if current position is within recordset bounds
+	 *
+	 * @return bool
+	 * @access public
+	 * @implements Iterator::valid
+	 */
+	public function valid()
+	{
+		return $this->position < $this->rowCount;
+	}
+
+	/**
+	 * Counts recordset rows
+	 *
+	 * @return int
+	 * @access public
+	 * @implements Countable::count
+	 */
+	public function count()
+	{
+		return $this->rowCount;
+	}
+
+	/**
+	 * Counts fields in current row
+	 *
+	 * @return int
+	 * @access public
+	 */
+	public function fieldCount()
+	{
+		return count($this->rowData);
+	}
+
+	/**
+	 * Moves cursor into given position within recordset
+	 *
+	 * @param int $position
+	 * @throws OutOfBoundsException
+	 * @access public
+	 * @implements SeekableIterator::seek
+	 */
+	public function seek($position)
+	{
+		if ( $this->position == $position ) {
+			return;
+		}
+
+		$this->position = $position;
+
+		if ( $this->valid() ) {
+			mysql_data_seek($this->result, $this->position);
+
+			$this->rowData = mysql_fetch_array($this->result);
+		}
+
+		/*if ( !$this->valid() ) {
+			throw new OutOfBoundsException('Invalid seek position (' . $position . ')');
+		}*/
+	}
+
+	/**
+	 * Returns first recordset row
+	 *
+	 * @return Array
+	 * @access public
+	 */
+	public function first()
+	{
+		$this->seek(0);
+
+		return $this->rowData;
+	}
+
+	/**
+	 * Closes recordset and freese memory
+	 *
+	 * @return void
+	 * @access public
+	 */
+	public function close()
+	{
+		mysql_free_result($this->result);
+		unset($this->result);
+	}
+
+	/**
+	 * Frees memory when object is destroyed
+	 *
+	 * @return void
+	 * @access public
+	 */
+	public function __destruct()
+	{
+		$this->close();
+	}
+
+	/**
+	 * Returns all keys
+	 *
+	 * @return Array
+	 * @access public
+	 */
+	public function keys()
+	{
+		$ret = Array ();
+
+		foreach ($this as $key => $value) {
+			$ret[] = $key;
+		}
+
+		return $ret;
+	}
+
+	/**
+	 * Returns all values
+	 *
+	 * @return Array
+	 * @access public
+	 */
+	public function values()
+	{
+		$ret = Array ();
+
+		foreach ($this as $value) {
+			$ret[] = $value;
+		}
+
+		return $ret;
+	}
+
+	/**
+	 * Returns whole recordset as array
+	 *
+	 * @return Array
+	 * @access public
+	 */
+	public function toArray()
+	{
+		$ret = Array ();
+
+		foreach ($this as $key => $value) {
+			$ret[$key] = $value;
+		}
+
+		return $ret;
+	}
+}
+
+
+class kMySQLQueryCol extends kMySQLQuery {
+
+	/**
+	 * Returns value at current position
+	 *
+	 * @return mixed
+	 * @access public
+	 * @implements Iterator::current
+	 */
+	function current()
+	{
+		return reset($this->rowData);
+	}
+
+	/**
+	 * Returns first column of first recordset row
+	 *
+	 * @return string
+	 * @access public
+	 */
+	public function first()
+	{
+		$this->seek(0);
+
+		return reset($this->rowData);
+	}
+}
Index: branches/5.2.x/core/kernel/application.php
===================================================================
--- branches/5.2.x/core/kernel/application.php	(revision 15238)
+++ branches/5.2.x/core/kernel/application.php	(revision 15239)
@@ -1,3158 +1,3159 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 /**
 * Basic class for Kernel4-based Application
 *
 * This class is a Facade for any other class which needs to deal with Kernel4 framework.<br>
 * The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.<br>
 * <br>
 * The class is a singleton, which means that there could be only one instance of kApplication in the script.<br>
 * This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method,
 * which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.<br>
 * See singleton pattern by GOF.
 */
 class kApplication implements kiCacheable {
 
 	/**
 	 * Location of module helper class (used in installator too)
 	 */
 	const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php';
 
 	/**
 	 * Is true, when Init method was called already, prevents double initialization
 	 *
 	 * @var bool
 	 */
 	public $InitDone = false;
 
 	/**
 	 * Holds internal NParser object
 	 *
 	 * @var NParser
 	 * @access public
 	 */
 	public $Parser;
 
 	/**
 	 * Holds parser output buffer
 	 *
 	 * @var string
 	 * @access protected
 	 */
 	protected $HTML = '';
 
 	/**
 	 * The main Factory used to create
 	 * almost any class of kernel and
 	 * modules
 	 *
 	 * @var kFactory
 	 * @access protected
 	 */
 	protected $Factory;
 
 	/**
 	 * Template names, that will be used instead of regular templates
 	 *
 	 * @var Array
 	 * @access public
 	 */
 	public $ReplacementTemplates = Array ();
 
 	/**
 	 * Mod-Rewrite listeners used during url building and parsing
 	 *
 	 * @var Array
 	 * @access public
 	 */
 	public $RewriteListeners = Array ();
 
 	/**
 	 * Reference to debugger
 	 *
 	 * @var Debugger
 	 * @access public
 	 */
 	public $Debugger = null;
 
 	/**
 	 * Holds all phrases used
 	 * in code and template
 	 *
 	 * @var PhrasesCache
 	 * @access public
 	 */
 	public $Phrases;
 
 	/**
 	 * Modules table content, key - module name
 	 *
 	 * @var Array
 	 * @access public
 	 */
 	public $ModuleInfo = Array ();
 
 	/**
 	 * Holds DBConnection
 	 *
 	 * @var kDBConnection
 	 * @access public
 	 */
 	public $Conn = null;
 
 	/**
 	 * Maintains list of user-defined error handlers
 	 *
 	 * @var Array
 	 * @access public
 	 */
 	public $errorHandlers = Array ();
 
 	/**
 	 * Maintains list of user-defined exception handlers
 	 *
 	 * @var Array
 	 * @access public
 	 */
 	public $exceptionHandlers = Array ();
 
 	// performance needs:
 	/**
 	 * Holds a reference to httpquery
 	 *
 	 * @var kHttpQuery
 	 * @access public
 	 */
 	public $HttpQuery = null;
 
 	/**
 	 * Holds a reference to UnitConfigReader
 	 *
 	 * @var kUnitConfigReader
 	 * @access public
 	 */
 	public $UnitConfigReader = null;
 
 	/**
 	 * Holds a reference to Session
 	 *
 	 * @var Session
 	 * @access public
 	 */
 	public $Session = null;
 
 	/**
 	 * Holds a ref to kEventManager
 	 *
 	 * @var kEventManager
 	 * @access public
 	 */
 	public $EventManager = null;
 
 	/**
 	 * Holds a ref to kUrlManager
 	 *
 	 * @var kUrlManager
 	 * @access public
 	 */
 	public $UrlManager = null;
 
 	/**
 	 * Ref for TemplatesCache
 	 *
 	 * @var TemplatesCache
 	 * @access public
 	 */
 	public $TemplatesCache = null;
 
 	/**
 	 * Holds current NParser tag while parsing, can be used in error messages to display template file and line
 	 *
 	 * @var _BlockTag
 	 * @access public
 	 */
 	public $CurrentNTag = null;
 
 	/**
 	 * Object of unit caching class
 	 *
 	 * @var kCacheManager
 	 * @access public
 	 */
 	public $cacheManager = null;
 
 	/**
 	 * Tells, that administrator has authenticated in administrative console
 	 * Should be used to manipulate data change OR data restrictions!
 	 *
 	 * @var bool
 	 * @access public
 	 */
 	public $isAdminUser = false;
 
 	/**
 	 * Tells, that admin version of "index.php" was used, nothing more!
 	 * Should be used to manipulate data display!
 	 *
 	 * @var bool
 	 * @access public
 	 */
 	public $isAdmin = false;
 
 	/**
 	 * Instance of site domain object
 	 *
 	 * @var kDBItem
 	 * @access public
 	 * @todo move away into separate module
 	 */
 	public $siteDomain = null;
 
 	/**
 	 * Prevent kApplication class to be created directly, only via Instance method
 	 *
 	 * @access private
 	 */
 	private function __construct()
 	{
 
 	}
 
 	final private function __clone() {}
 
 	/**
 	 * Returns kApplication instance anywhere in the script.
 	 *
 	 * This method should be used to get single kApplication object instance anywhere in the
 	 * Kernel-based application. The method is guaranteed to return the SAME instance of kApplication.
 	 * Anywhere in the script you could write:
 	 * <code>
 	 *		$application =& kApplication::Instance();
 	 * </code>
 	 * or in an object:
 	 * <code>
 	 *		$this->Application =& kApplication::Instance();
 	 * </code>
 	 * to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class.
 	 * To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant
 	 * BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would
 	 * create and return default KernelApplication instance.
 	 *
 	 * Pattern: Singleton
 	 *
 	 * @static
 	 * @return kApplication
 	 * @access public
 	 */
 	public static function &Instance()
 	{
 		static $instance = false;
 
 		if ( !$instance ) {
 			$class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication';
 			$instance = new $class();
 		}
 
 		return $instance;
 	}
 
 	/**
 	 * Initializes the Application
 	 *
 	 * @param string $factory_class
 	 * @return bool Was Init actually made now or before
 	 * @access public
 	 * @see kHTTPQuery
 	 * @see Session
 	 * @see TemplatesCache
 	 */
 	public function Init($factory_class = 'kFactory')
 	{
 		if ( $this->InitDone ) {
 			return false;
 		}
 
 		$this->isAdmin = kUtil::constOn('ADMIN');
 
 		if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
 			ob_start(); // collect any output from method (other then tags) into buffer
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Init:');
 		}
 
 		if ( !$this->isDebugMode() && !kUtil::constOn('DBG_ZEND_PRESENT') ) {
 			error_reporting(0);
 			ini_set('display_errors', 0);
 		}
 
 		if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) {
 			$error_handler = set_error_handler(Array (&$this, 'handleError'));
 			if ( $error_handler ) {
 				// wrap around previous error handler, if any was set
 				$this->errorHandlers[] = $error_handler;
 			}
 
 			$exception_handler = set_exception_handler(Array (&$this, 'handleException'));
 			if ( $exception_handler ) {
 				// wrap around previous exception handler, if any was set
 				$this->exceptionHandlers[] = $exception_handler;
 			}
 		}
 
 		$this->Factory = new $factory_class();
 		$this->registerDefaultClasses();
 
 		$vars = kUtil::parseConfig(true);
-		$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : 'kDBConnection';
+		$db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection');
 		$this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array (&$this, 'handleSQLError')));
 		$this->Conn->setup($vars);
 
 		$this->cacheManager = $this->makeClass('kCacheManager');
 		$this->cacheManager->InitCache();
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Before UnitConfigReader');
 		}
 
 		// init config reader and all managers
 		$this->UnitConfigReader = $this->makeClass('kUnitConfigReader');
 		$this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read
 
 		$this->registerModuleConstants();
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('After UnitConfigReader');
 		}
 
 		define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0);
 
 		// start processing request
 		$this->HttpQuery = $this->recallObject('HTTPQuery');
 		$this->HttpQuery->process();
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery initial');
 		}
 
 		$this->Session = $this->recallObject('Session');
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed Session');
 		}
 
 		$this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit');
 		}
 
 		$this->cacheManager->LoadApplicationCache();
 
 		$site_timezone = $this->ConfigValue('Config_Site_Time');
 
 		if ( $site_timezone ) {
 			putenv('TZ=' . $site_timezone);
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Loaded cache and phrases');
 		}
 
 		$this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there
 
 		$this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->appendTimestamp('Processed AfterConfigRead');
 		}
 
 		if ( $this->GetVar('m_cat_id') === false ) {
 			$this->SetVar('m_cat_id', 0);
 		}
 
 		if ( !$this->RecallVar('curr_iso') ) {
 			$this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional
 		}
 
 		$visit_id = $this->RecallVar('visit_id');
 
 		if ( $visit_id !== false ) {
 			$this->SetVar('visits_id', $visit_id);
 		}
 
 		$language = $this->recallObject('lang.current', null, Array ('live_table' => true));
 		/* @var $language LanguagesItem */
 
 		if ( preg_match('/utf-8/', $language->GetDBField('Charset')) ) {
 			setlocale(LC_ALL, 'en_US.UTF-8');
 			mb_internal_encoding('UTF-8');
 		}
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 			$this->Debugger->profileFinish('kernel4_startup');
 		}
 
 		$this->InitDone = true;
 
 		$this->HandleEvent(new kEvent('adm:OnStartup'));
 
 		return true;
 	}
 
 	/**
 	 * Performs initialization of manager classes, that can be overridden from unit configs
 	 *
 	 * @return void
 	 * @access public
 	 * @throws Exception
 	 */
 	public function InitManagers()
 	{
 		if ( $this->InitDone ) {
 			throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR);
 			return;
 		}
 
 		$this->UrlManager = $this->makeClass('kUrlManager');
 		$this->EventManager = $this->makeClass('EventManager');
 		$this->Phrases = $this->makeClass('kPhraseCache');
 
 		$this->RegisterDefaultBuildEvents();
 	}
 
 	/**
 	 * Returns module information. Searches module by requested field
 	 *
 	 * @param string $field
 	 * @param mixed $value
 	 * @param string $return_field field value to returns, if not specified, then return all fields
 	 * @return Array
 	 */
 	public function findModule($field, $value, $return_field = null)
 	{
 		$found = $module_info = false;
 
 		foreach ($this->ModuleInfo as $module_info) {
 			if ( strtolower($module_info[$field]) == strtolower($value) ) {
 				$found = true;
 				break;
 			}
 		}
 
 		if ( $found ) {
 			return isset($return_field) ? $module_info[$return_field] : $module_info;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Refreshes information about loaded modules
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function refreshModuleInfo()
 	{
 		if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) {
 			$this->registerModuleConstants();
 			return;
 		}
 
 		// use makeClass over recallObject, since used before kApplication initialization during installation
 		$modules_helper = $this->makeClass('ModulesHelper');
 		/* @var $modules_helper kModulesHelper */
 
 		$this->Conn->nextQueryCachable = true;
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'Modules
 				WHERE ' . $modules_helper->getWhereClause() . '
 				ORDER BY LoadOrder';
 		$this->ModuleInfo = $this->Conn->Query($sql, 'Name');
 
 		$this->registerModuleConstants();
 	}
 
 	/**
 	 * Checks if passed language id if valid and sets it to primary otherwise
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public 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
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function VerifyThemeId()
 	{
 		if ( $this->isAdmin ) {
 			kUtil::safeDefine('THEMES_PATH', '/core/admin_templates');
 
 			return;
 		}
 
 		$path = $this->GetFrontThemePath();
 
 		if ( $path === false ) {
 			$this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled');
 		}
 
 		kUtil::safeDefine('THEMES_PATH', $path);
 	}
 
 	/**
 	 * Returns relative path to current front-end theme
 	 *
 	 * @param bool $force
 	 * @return string
 	 * @access public
 	 */
 	public function GetFrontThemePath($force = false)
 	{
 		static $path = null;
 
 		if ( !$force && isset($path) ) {
 			return $path;
 		}
 
 		$theme_id = $this->GetVar('m_theme');
 		if ( !$theme_id ) {
 			$theme_id = 'default'; // $this->GetDefaultThemeId(1); // 1 to force front-end mode!
 		}
 
 		$this->SetVar('m_theme', $theme_id);
 		$this->SetVar('theme.current_id', $theme_id); // KOSTJA: this is to fool theme' getPassedID
 
 		$theme = $this->recallObject('theme.current');
 		/* @var $theme ThemeItem */
 
 		if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) {
 			return false;
 		}
 
 		// assign & then return, since it's static variable
 		$path = '/themes/' . $theme->GetDBField('Name');
 
 		return $path;
 	}
 
 	/**
 	 * Returns primary front/admin language id
 	 *
 	 * @param bool $init
 	 * @return int
 	 * @access public
 	 */
 	public function GetDefaultLanguageId($init = false)
 	{
 		$cache_key = 'primary_language_info[%LangSerial%]';
 		$language_info = $this->getCache($cache_key);
 
 		if ( $language_info === false ) {
 			// cache primary language info first
 			$table = $this->getUnitOption('lang', 'TableName');
 			$id_field = $this->getUnitOption('lang', 'IDField');
 
 			$this->Conn->nextQueryCachable = true;
 			$sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey
 					FROM ' . $table . '
 					WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)';
 			$language_info = $this->Conn->GetCol($sql, 'LanguageKey');
 
 			if ( $language_info !== false ) {
 				$this->setCache($cache_key, $language_info);
 			}
 		}
 
 		$language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front';
 
 		if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) {
 			// get from cache
 			return $language_info[$language_key];
 		}
 
 		$language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false;
 
 		if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) {
 			$language_id = 1;
 		}
 
 		return $language_id;
 	}
 
 	/**
 	 * Returns front-end primary theme id (even, when called from admin console)
 	 *
 	 * @param bool $force_front
 	 * @return int
 	 * @access public
 	 */
 	public function GetDefaultThemeId($force_front = false)
 	{
 		static $theme_id = 0;
 
 		if ( $theme_id > 0 ) {
 			return $theme_id;
 		}
 
 		if ( kUtil::constOn('DBG_FORCE_THEME') ) {
 			$theme_id = DBG_FORCE_THEME;
 		}
 		elseif ( !$force_front && $this->isAdmin ) {
 			$theme_id = 999;
 		}
 		else {
 			$cache_key = 'primary_theme[%ThemeSerial%]';
 			$theme_id = $this->getCache($cache_key);
 
 			if ( $theme_id === false ) {
 				$this->Conn->nextQueryCachable = true;
 				$sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . '
 						FROM ' . $this->getUnitOption('theme', 'TableName') . '
 						WHERE (PrimaryTheme = 1) AND (Enabled = 1)';
 				$theme_id = $this->Conn->GetOne($sql);
 
 				if ( $theme_id !== false ) {
 					$this->setCache($cache_key, $theme_id);
 				}
 			}
 		}
 
 		return $theme_id;
 	}
 
 	/**
 	 * Returns site primary currency ISO code
 	 *
 	 * @return string
 	 * @access public
 	 * @todo Move into In-Commerce
 	 */
 	public function GetPrimaryCurrency()
 	{
 		$cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId');
 		$currency_iso = $this->getCache($cache_key);
 
 		if ( $currency_iso === false ) {
 			if ( $this->isModuleEnabled('In-Commerce') ) {
 				$this->Conn->nextQueryCachable = true;
 				$currency_id = $this->siteDomainField('PrimaryCurrencyId');
 
 				$sql = 'SELECT ISO
 						FROM ' . $this->getUnitOption('curr', 'TableName') . '
 						WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1');
 				$currency_iso = $this->Conn->GetOne($sql);
 			}
 			else {
 				$currency_iso = 'USD';
 			}
 
 			$this->setCache($cache_key, $currency_iso);
 		}
 
 		return $currency_iso;
 	}
 
 	/**
 	 * Returns site domain field. When none of site domains are found false is returned.
 	 *
 	 * @param string $field
 	 * @param bool $formatted
 	 * @param string $format
 	 * @return mixed
 	 * @todo Move into separate module
 	 */
 	public function siteDomainField($field, $formatted = false, $format = null)
 	{
 		if ( $this->isAdmin ) {
 			// don't apply any filtering in administrative console
 			return false;
 		}
 
 		if ( !$this->siteDomain ) {
 			$this->siteDomain = $this->recallObject('site-domain.current');
 			/* @var $site_domain kDBItem */
 		}
 
 		if ( $this->siteDomain->isLoaded() ) {
 			return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field);
 		}
 
 		return false;
 	}
 
 	/**
 	 * Registers default classes such as kDBEventHandler, kUrlManager
 	 *
 	 * Called automatically while initializing kApplication
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function RegisterDefaultClasses()
 	{
 		$this->registerClass('kHelper', KERNEL_PATH . '/kbase.php');
 		$this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php');
 		$this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php');
 
 		$this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager');
 		$this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php');
 		$this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php');
 		$this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php');
 
 		$this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php');
 		$this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php');
 		$this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php');
 		$this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php');
 
 		$this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php');
 		$this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache');
 		$this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php');
 		$this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php');
 		$this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php');
 
 		$this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php');
 
 		// Params class descendants
 		$this->registerClass('kArray', KERNEL_PATH . '/utility/params.php');
 		$this->registerClass('Params', KERNEL_PATH . '/utility/params.php');
 		$this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions');
 		$this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params');
 		$this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery');
 
 		// session
 		$this->registerClass('Session', KERNEL_PATH . '/session/session.php');
 		$this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php');
 		$this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session');
 		$this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage');
 
 		// template parser
 		$this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php');
 		$this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor');
 		$this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.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');
 
 		// database
 		$this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php');
+		$this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php');
 		$this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php');
 		$this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php');
 		$this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php');
 		$this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php');
 		$this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php');
 		$this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php');
 		$this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php');
 
 		// email sending
 		$this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php');
 		$this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender');
 		$this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket');
 
 		// do not move to config - this helper is used before configs are read
 		$this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper');
 	}
 
 	/**
 	 * Registers default build events
 	 *
 	 * @return void
 	 * @access protected
 	 */
 	protected function RegisterDefaultBuildEvents()
 	{
 		$this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild');
 	}
 
 	/**
 	 * Returns cached category information by given cache name. All given category
 	 * information is recached, when at least one of 4 caches is missing.
 	 *
 	 * @param int $category_id
 	 * @param string $name cache name = {filenames, category_designs, category_tree}
 	 * @return string
 	 * @access public
 	 */
 	public function getCategoryCache($category_id, $name)
 	{
 		return $this->cacheManager->getCategoryCache($category_id, $name);
 	}
 
 	/**
 	 * Returns caching type (none, memory, temporary)
 	 *
 	 * @param int $caching_type
 	 * @return bool
 	 * @access public
 	 */
 	public function isCachingType($caching_type)
 	{
 		return $this->cacheManager->isCachingType($caching_type);
 	}
 
 	/**
 	 * Increments serial based on prefix and it's ID (optional)
 	 *
 	 * @param string $prefix
 	 * @param int $id ID (value of IDField) or ForeignKeyField:ID
 	 * @param bool $increment
 	 * @return string
 	 * @access public
 	 */
 	public function incrementCacheSerial($prefix, $id = null, $increment = true)
 	{
 		return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment);
 	}
 
 	/**
 	 * Returns cached $key value from cache named $cache_name
 	 *
 	 * @param int $key key name from cache
 	 * @param bool $store_locally store data locally after retrieved
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0)
 	{
 		return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds);
 	}
 
 	/**
 	 * Stores new $value in cache with $key name
 	 *
 	 * @param int $key key name to add to cache
 	 * @param mixed $value value of cached record
 	 * @param int $expiration when value expires (0 - doesn't expire)
 	 * @return bool
 	 * @access public
 	 */
 	public function setCache($key, $value, $expiration = 0)
 	{
 		return $this->cacheManager->setCache($key, $value, $expiration);
 	}
 
 	/**
 	 * Stores new $value in cache with $key name (only if it's not there)
 	 *
 	 * @param int $key key name to add to cache
 	 * @param mixed $value value of cached record
 	 * @param int $expiration when value expires (0 - doesn't expire)
 	 * @return bool
 	 * @access public
 	 */
 	public function addCache($key, $value, $expiration = 0)
 	{
 		return $this->cacheManager->addCache($key, $value, $expiration);
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 * @return bool
 	 * @access public
 	 */
 	public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time);
 	}
 
 	/**
 	 * Deletes key from cache
 	 *
 	 * @param string $key
 	 * @return void
 	 * @access public
 	 */
 	public function deleteCache($key)
 	{
 		$this->cacheManager->deleteCache($key);
 	}
 
 	/**
 	 * Reset's all memory cache at once
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function resetCache()
 	{
 		$this->cacheManager->resetCache();
 	}
 
 	/**
 	 * Returns value from database cache
 	 *
 	 * @param string $name key name
 	 * @param int $max_rebuild_seconds
 	 * @return mixed
 	 * @access public
 	 */
 	public function getDBCache($name, $max_rebuild_seconds = 0)
 	{
 		return $this->cacheManager->getDBCache($name, $max_rebuild_seconds);
 	}
 
 	/**
 	 * Sets value to database cache
 	 *
 	 * @param string $name
 	 * @param mixed $value
 	 * @param int|bool $expiration
 	 * @return void
 	 * @access public
 	 */
 	public function setDBCache($name, $value, $expiration = false)
 	{
 		$this->cacheManager->setDBCache($name, $value, $expiration);
 	}
 
 	/**
 	 * Sets rebuilding mode for given cache
 	 *
 	 * @param string $name
 	 * @param int $mode
 	 * @param int $max_rebuilding_time
 	 * @return bool
 	 * @access public
 	 */
 	public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0)
 	{
 		return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time);
 	}
 
 	/**
 	 * Deletes key from database cache
 	 *
 	 * @param string $name
 	 * @return void
 	 * @access public
 	 */
 	public function deleteDBCache($name)
 	{
 		$this->cacheManager->deleteDBCache($name);
 	}
 
 	/**
 	 * Registers each module specific constants if any found
 	 *
 	 * @return bool
 	 * @access protected
 	 */
 	protected function registerModuleConstants()
 	{
 		if ( file_exists(KERNEL_PATH . '/constants.php') ) {
 			kUtil::includeOnce(KERNEL_PATH . '/constants.php');
 		}
 
 		if ( !$this->ModuleInfo ) {
 			return false;
 		}
 
 		foreach ($this->ModuleInfo as $module_info) {
 			$constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php';
 
 			if ( file_exists($constants_file) ) {
 				kUtil::includeOnce($constants_file);
 			}
 		}
 
 		return true;
 	}
 
 	/**
 	 * Performs redirect to hard maintenance template
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function redirectToMaintenance()
 	{
 		$maintenance_page = WRITEBALE_BASE . '/maintenance.html';
 		$query_string = ''; // $this->isAdmin ? '' : '?next_template=' . urlencode($_SERVER['REQUEST_URI']);
 
 		if ( file_exists(FULL_PATH . $maintenance_page) ) {
 			header('Location: ' . BASE_PATH . $maintenance_page . $query_string);
 			exit;
 		}
 	}
 
 	/**
 	 * 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.
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function Run()
 	{
 		// process maintenance mode redirect: begin
 		$maintenance_mode = $this->getMaintenanceMode();
 
 		if ( $maintenance_mode == MaintenanceMode::HARD ) {
 			$this->redirectToMaintenance();
 		}
 		elseif ( $maintenance_mode == MaintenanceMode::SOFT ) {
 			$maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate');
 
 			if ( $this->GetVar('t') != $maintenance_template ) {
 				$redirect_params = Array ();
 
 				if ( !$this->isAdmin ) {
 					$redirect_params['next_template'] = urlencode($_SERVER['REQUEST_URI']);
 				}
 
 				$this->Redirect($maintenance_template, $redirect_params);
 			}
 		}
 		// process maintenance mode redirect: end
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Run:');
 		}
 
 		if ( $this->isAdminUser ) {
 			// for permission checking in events & templates
 			$this->LinkVar('module'); // for common configuration templates
 			$this->LinkVar('module_key'); // for common search templates
 			$this->LinkVar('section'); // for common configuration templates
 
 			if ( $this->GetVar('m_opener') == 'p' ) {
 				$this->LinkVar('main_prefix'); // window prefix, that opened selector
 				$this->LinkVar('dst_field'); // field to set value choosed in selector
 			}
 
 			if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) {
 				// hide debug output from ajax requests automatically
 				kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
 			}
 		}
 		elseif ( $this->GetVar('admin') ) {
 			$admin_session = $this->recallObject('Session.admin');
 			/* @var $admin_session Session */
 
 			// store Admin Console User's ID to Front-End's session for cross-session permission checks
 			$this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
 
 			if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
 				// user can edit cms blocks (when viewing front-end through admin's frame)
 				$editing_mode = $this->GetVar('editing_mode');
 				define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
 			}
 		}
 
 		kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything
 		$this->Phrases->setPhraseEditing();
 
 		$this->EventManager->ProcessRequest();
 
 		$this->InitParser();
 		$t = $this->GetVar('render_template', $this->GetVar('t'));
 
 		if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) {
 			$cms_handler = $this->recallObject('st_EventHandler');
 			/* @var $cms_handler CategoriesEventHandler */
 
 			$t = ltrim($cms_handler->GetDesignTemplate(), '/');
 
 			if ( defined('DEBUG_MODE') && $this->isDebugMode() ) {
 				$this->Debugger->appendHTML('<strong>Design Template</strong>: ' . $t . '; <strong>CategoryID</strong>: ' . $this->GetVar('m_cat_id'));
 			}
 		}
 		/*else {
 			$cms_handler->SetCatByTemplate();
 		}*/
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Parsing:');
 		}
 
 		$this->HTML = $this->Parser->Run($t);
 
 		if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application after Parsing:');
 		}
 	}
 
 	/**
 	 * Only renders template
 	 *
 	 * @see kDBEventHandler::_errorNotFound()
 	 */
 	public function QuickRun()
 	{
 		$this->InitParser();
 		$this->HTML = $this->ParseBlock(Array ('name' => $this->GetVar('t')));
 	}
 
 	/**
 	 * Performs template parser/cache initialization
 	 *
 	 * @param bool|string $theme_name
 	 * @return void
 	 * @access public
 	 */
 	public 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.
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function Done()
 	{
 		$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
 
 		$debug_mode = defined('DEBUG_MODE') && $this->isDebugMode();
 
 		if ( $debug_mode && kUtil::constOn('DBG_PROFILE_MEMORY') ) {
 			$this->Debugger->appendMemoryUsage('Application before Done:');
 		}
 
 		if ( $debug_mode ) {
 			$this->EventManager->runScheduledTasks();
 			$this->Session->SaveData();
 
 			$this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true);
 		}
 		else {
 			// send "Set-Cookie" header before any output is made
 			$this->Session->SetSession();
 
 			$this->HTML = ob_get_clean() . $this->HTML;
 		}
 
 		$this->setContentType();
 
 		if ( $this->UseOutputCompression() ) {
 			$compression_level = $this->ConfigValue('OutputCompressionLevel');
 
 			if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) {
 				$compression_level = 7;
 			}
 
 			header('Content-Encoding: gzip');
 			echo gzencode($this->HTML, $compression_level);
 		}
 		else {
 			echo $this->HTML;
 		}
 
 		$this->cacheManager->UpdateApplicationCache();
 		flush();
 
 		if ( !$debug_mode ) {
 			$this->EventManager->runScheduledTasks();
 			$this->Session->SaveData();
 		}
 
 		if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) {
 			$this->_storeStatistics();
 		}
 	}
 
 	/**
 	 * Stores script execution statistics to database
 	 *
 	 * @return void
 	 * @access protected
 	 */
 	protected function _storeStatistics()
 	{
 		global $start;
 
 		$script_time = microtime(true) - $start;
 		$query_statistics = $this->Conn->getQueryStatistics(); // time & count
 
 		$sql = 'SELECT *
 				FROM ' . TABLE_PREFIX . 'StatisticsCapture
 				WHERE TemplateName = ' . $this->Conn->qstr($this->GetVar('t'));
 		$data = $this->Conn->GetRow($sql);
 
 		if ( $data ) {
 			$this->_updateAverageStatistics($data, 'ScriptTime', $script_time);
 			$this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']);
 			$this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']);
 
 			$data['Hits']++;
 			$data['LastHit'] = adodb_mktime();
 
 			$this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']);
 		}
 		else {
 			$data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time;
 			$data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time'];
 			$data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count'];
 			$data['TemplateName'] = $this->GetVar('t');
 			$data['Hits'] = 1;
 			$data['LastHit'] = adodb_mktime();
 			$this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture');
 		}
 	}
 
 	/**
 	 * Calculates average time for statistics
 	 *
 	 * @param Array $data
 	 * @param string $field_prefix
 	 * @param float $current_value
 	 * @return void
 	 * @access protected
 	 */
 	protected 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;
 		}
 	}
 
 	/**
 	 * Remembers slow query SQL and execution time into log
 	 *
 	 * @param string $slow_sql
 	 * @param int $time
 	 * @return void
 	 * @access public
 	 */
 	public 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 bool
 	 * @access protected
 	 */
 	protected function UseOutputCompression()
 	{
 		if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) {
 			return false;
 		}
 
 		return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
 	}
 
 	//	Facade
 
 	/**
 	 * Returns current session id (SID)
 	 *
 	 * @return int
 	 * @access public
 	 */
 	public function GetSID()
 	{
 		$session = $this->recallObject('Session');
 		/* @var $session Session */
 
 		return $session->GetID();
 	}
 
 	/**
 	 * Destroys current session
 	 *
 	 * @return void
 	 * @access public
 	 * @see UserHelper::logoutUser()
 	 */
 	public function DestroySession()
 	{
 		$session = $this->recallObject('Session');
 		/* @var $session Session */
 
 		$session->Destroy();
 	}
 
 	/**
 	 * Returns variable passed to the script as GET/POST/COOKIE
 	 *
 	 * @param string $name Name of variable to retrieve
 	 * @param mixed $default default value returned in case if variable not present
 	 * @return mixed
 	 * @access public
 	 */
 	public function GetVar($name, $default = false)
 	{
 		return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default;
 	}
 
 	/**
 	 * Returns variable passed to the script as $type
 	 *
 	 * @param string $name Name of variable to retrieve
 	 * @param string $type Get/Post/Cookie
 	 * @param mixed $default default value returned in case if variable not present
 	 * @return mixed
 	 * @access public
 	 */
 	public function GetVarDirect($name, $type, $default = false)
 	{
 //		$type = ucfirst($type);
 		$array = $this->HttpQuery->$type;
 
 		return isset($array[$name]) ? $array[$name] : $default;
 	}
 
 	/**
 	 * Returns ALL variables passed to the script as GET/POST/COOKIE
 	 *
 	 * @return Array
 	 * @access public
 	 * @deprecated
 	 */
 	public 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>
 	 *
 	 * @param string $var Variable name to set
 	 * @param mixed $val Variable value
 	 * @return void
 	 * @access public
 	 */
 	public function SetVar($var,$val)
 	{
 		$this->HttpQuery->Set($var, $val);
 	}
 
 	/**
 	 * Deletes kHTTPQuery variable
 	 *
 	 * @param string $var
 	 * @return void
 	 * @todo Think about method name
 	 */
 	public function DeleteVar($var)
 	{
 		$this->HttpQuery->Remove($var);
 	}
 
 	/**
 	 * Deletes Session variable
 	 *
 	 * @param string $var
 	 * @return void
 	 * @access public
 	 */
 	public function RemoveVar($var)
 	{
 		$this->Session->RemoveVar($var);
 	}
 
 	/**
 	 * Removes variable from persistent session
 	 *
 	 * @param string $var
 	 * @return void
 	 * @access public
 	 */
 	public function RemovePersistentVar($var)
 	{
 		$this->Session->RemovePersistentVar($var);
 	}
 
 	/**
 	 * Restores Session variable to it's db version
 	 *
 	 * @param string $var
 	 * @return void
 	 * @access public
 	 */
 	public function RestoreVar($var)
 	{
 		$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.
 	 *
 	 * @param string $var Variable name
 	 * @param mixed $default Default value to return if no $var variable found in session
 	 * @return mixed
 	 * @access public
 	 * @see Session::RecallVar()
 	 */
 	public function RecallVar($var,$default=false)
 	{
 		return $this->Session->RecallVar($var,$default);
 	}
 
 	/**
 	 * Returns variable value from persistent session
 	 *
 	 * @param string $var
 	 * @param mixed $default
 	 * @return mixed
 	 * @access public
 	 * @see Session::RecallPersistentVar()
 	 */
 	public function RecallPersistentVar($var, $default = false)
 	{
 		return $this->Session->RecallPersistentVar($var, $default);
 	}
 
 	/**
 	 * Stores variable $val in session under name $var
 	 *
 	 * Use this method to store variable in session. Later this variable could be recalled.
 	 *
 	 * @param string $var Variable name
 	 * @param mixed $val Variable value
 	 * @param bool $optional
 	 * @return void
 	 * @access public
 	 * @see kApplication::RecallVar()
 	 */
 	public function StoreVar($var, $val, $optional = false)
 	{
 		$session = $this->recallObject('Session');
 		/* @var $session Session */
 
 		$this->Session->StoreVar($var, $val, $optional);
 	}
 
 	/**
 	 * Stores variable to persistent session
 	 *
 	 * @param string $var
 	 * @param mixed $val
 	 * @param bool $optional
 	 * @return void
 	 * @access public
 	 */
 	public function StorePersistentVar($var, $val, $optional = false)
 	{
 		$this->Session->StorePersistentVar($var, $val, $optional);
 	}
 
 	/**
 	 * Stores default value for session variable
 	 *
 	 * @param string $var
 	 * @param string $val
 	 * @param bool $optional
 	 * @return void
 	 * @access public
 	 * @see Session::RecallVar()
 	 * @see Session::StoreVar()
 	 */
 	public function StoreVarDefault($var, $val, $optional = false)
 	{
 		$session = $this->recallObject('Session');
 		/* @var $session 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
 	 *
 	 * @param string $var HTTP Query (GPC) variable name
 	 * @param mixed $ses_var Session variable name
 	 * @param mixed $default Default variable value
 	 * @param bool $optional
 	 * @return void
 	 * @access public
 	 */
 	public 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
 	 *
 	 * @param string $var HTTP Query (GPC) variable name
 	 * @param mixed $ses_var Session variable name
 	 * @param mixed $default Default variable value
 	 * @return mixed
 	 * @access public
 	 * @see LinkVar
 	 */
 	public function GetLinkedVar($var, $ses_var = null, $default = '')
 	{
 		$this->LinkVar($var, $ses_var, $default);
 
 		return $this->GetVar($var);
 	}
 
 	/**
 	 * Renders given tag and returns it's output
 	 *
 	 * @param string $prefix
 	 * @param string $tag
 	 * @param Array $params
 	 * @return mixed
 	 * @access public
 	 * @see kApplication::InitParser()
 	 */
 	public function ProcessParsedTag($prefix, $tag, $params)
 	{
 		$processor = $this->Parser->GetProcessor($prefix);
 		/* @var $processor kDBTagProcessor */
 
 		return $processor->ProcessParsedTag($tag, $params, $prefix);
 	}
 
 	/**
 	 * Return ADODB Connection object
 	 *
 	 * Returns ADODB Connection object already connected to the project database, configurable in config.php
 	 *
 	 * @return kDBConnection
 	 * @access public
 	 */
 	public function &GetADODBConnection()
 	{
 		return $this->Conn;
 	}
 
 	/**
 	 * Allows to parse given block name or include template
 	 *
 	 * @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name.
 	 * @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter.
 	 * @param bool $as_template
 	 * @return string
 	 * @access public
 	 */
 	public function ParseBlock($params, $pass_params = false, $as_template = false)
 	{
 		if ( substr($params['name'], 0, 5) == 'html:' ) {
 			return substr($params['name'], 5);
 		}
 
 		return $this->Parser->ParseBlock($params, $pass_params, $as_template);
 	}
 
 	/**
 	 * Checks, that we have given block defined
 	 *
 	 * @param string $name
 	 * @return bool
 	 * @access public
 	 */
 	public function ParserBlockFound($name)
 	{
 		return $this->Parser->blockFound($name);
 	}
 
 	/**
 	 * Allows to include template with a given name and given parameters
 	 *
 	 * @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name.
 	 * @return string
 	 * @access public
 	 */
 	public function IncludeTemplate($params)
 	{
 		return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0);
 	}
 
 	/**
 	 * Return href for template
 	 *
 	 * @param string $t Template path
 	 * @param string $prefix index.php prefix - could be blank, 'admin'
 	 * @param Array $params
 	 * @param string $index_file
 	 * @return string
 	 */
 	public function HREF($t, $prefix = '', $params = Array (), $index_file = null)
 	{
 		return $this->UrlManager->HREF($t, $prefix, $params, $index_file);
 	}
 
 	/**
 	 * Returns theme template filename and it's corresponding page_id based on given seo template
 	 *
 	 * @param string $seo_template
 	 * @return string
 	 * @access public
 	 */
 	public function getPhysicalTemplate($seo_template)
 	{
 		return $this->UrlManager->getPhysicalTemplate($seo_template);
 	}
 
 	/**
 	 * Returns template name, that corresponds with given virtual (not physical) page id
 	 *
 	 * @param int $page_id
 	 * @return string|bool
 	 * @access public
 	 */
 	public function getVirtualPageTemplate($page_id)
 	{
 		return $this->UrlManager->getVirtualPageTemplate($page_id);
 	}
 
 	/**
 	 * Returns variables with values that should be passed through with this link + variable list
 	 *
 	 * @param Array $params
 	 * @return Array
 	 * @access public
 	 */
 	public function getPassThroughVariables(&$params)
 	{
 		return $this->UrlManager->getPassThroughVariables($params);
 	}
 
 	/**
 	 * Builds url
 	 *
 	 * @param string $t
 	 * @param Array $params
 	 * @param string $pass
 	 * @param bool $pass_events
 	 * @param bool $env_var
 	 * @return string
 	 * @access public
 	 */
 	public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true)
 	{
 		return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var);
 	}
 
 	/**
 	 * Process QueryString only, create
 	 * events, ids, based on config
 	 * set template name and sid in
 	 * desired application variables.
 	 *
 	 * @param string $env_var environment string value
 	 * @param string $pass_name
 	 * @return Array
 	 * @access public
 	 */
 	public function processQueryString($env_var, $pass_name = 'passed')
 	{
 		return $this->UrlManager->plain->parse($env_var, $pass_name);
 	}
 
 	/**
 	 * Returns base part of all urls, build on website
 	 *
 	 * @param string $prefix
 	 * @param bool $ssl
 	 * @param bool $add_port
 	 * @return string
 	 * @access public
 	 */
 	public function BaseURL($prefix = '', $ssl = null, $add_port = true)
 	{
 		if ( $ssl === null ) {
 			// stay on same encryption level
 			return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
 		}
 
 		if ( $ssl ) {
 			// going from http:// to https://
 			$base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false;
 
 			if ( !$base_url ) {
 				$ssl_url = $this->siteDomainField('SSLUrl');
 				$base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL');
 			}
 
 			return rtrim($base_url, '/') . $prefix . '/';
 		}
 
 		// going from https:// to http://
 		$domain = $this->siteDomainField('DomainName');
 
 		if ( $domain === false ) {
 			$domain = DOMAIN;
 		}
 
 		return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/';
 	}
 
 	/**
 	 * Redirects user to url, that's build based on given parameters
 	 *
 	 * @param string $t
 	 * @param Array $params
 	 * @param string $prefix
 	 * @param string $index_file
 	 * @return void
 	 * @access public
 	 */
 	public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null)
 	{
 		$js_redirect = getArrayValue($params, 'js_redirect');
 
 		if ( $t == '' || $t === true ) {
 			$t = $this->GetVar('t');
 		}
 
 		// pass prefixes and special from previous url
 		if ( array_key_exists('js_redirect', $params) ) {
 			unset($params['js_redirect']);
 		}
 
 		// allows to send custom responce code along with redirect header
 		if ( array_key_exists('response_code', $params) ) {
 			$response_code = (int)$params['response_code'];
 			unset($params['response_code']);
 		}
 		else {
 			$response_code = 302; // Found
 		}
 
 		if ( !array_key_exists('pass', $params) ) {
 			$params['pass'] = 'all';
 		}
 
 		if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) {
 			// redirects to the same template as current
 			$params['ajax'] = 'yes';
 		}
 
 		$params['__URLENCODE__'] = 1;
 		$location = $this->HREF($t, $prefix, $params, $index_file);
 
 		if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) {
 			$this->Debugger->appendTrace();
 			echo '<strong>Debug output above !!!</strong><br/>' . "\n";
 
 			if ( array_key_exists('HTTP_REFERER', $_SERVER) ) {
 				echo 'Referer: <strong>' . $_SERVER['HTTP_REFERER'] . '</strong><br/>' . "\n";
 			}
 
 			echo "Proceed to redirect: <a href=\"{$location}\">{$location}</a><br/>\n";
 		}
 		else {
 			if ( $js_redirect ) {
 				// show "redirect" template instead of redirecting,
 				// because "Set-Cookie" header won't work, when "Location"
 				// header is used later
 				$this->SetVar('t', 'redirect');
 				$this->SetVar('redirect_to', $location);
 
 				// make all additional parameters available on "redirect" template too
 				foreach ($params as $name => $value) {
 					$this->SetVar($name, $value);
 				}
 
 				return;
 			}
 			else {
 				if ( $this->GetVar('ajax') == 'yes' && $t != $this->GetVar('t') ) {
 					// redirection to other then current template during ajax request
 					kUtil::safeDefine('DBG_SKIP_REPORTING', 1);
 					echo '#redirect#' . $location;
 				}
 				elseif ( headers_sent() != '' ) {
 					// some output occurred -> redirect using javascript
 					echo '<script type="text/javascript">window.location.href = \'' . $location . '\';</script>';
 				}
 				else {
 					// no output before -> redirect using HTTP header
 
 //					header('HTTP/1.1 302 Found');
 					header('Location: ' . $location, true, $response_code);
 				}
 			}
 		}
 
 		// session expiration is called from session initialization,
 		// that's why $this->Session may be not defined here
 		$session = $this->recallObject('Session');
 		/* @var $session Session */
 
 		$this->HandleEvent(new kEvent('adm:OnBeforeShutdown'));
 		$session->SaveData();
 
 		ob_end_flush();
 		exit;
 	}
 
 	/**
 	 * Returns translation of given label
 	 *
 	 * @param string $label
 	 * @param bool $allow_editing return translation link, when translation is missing on current language
 	 * @param bool $use_admin use current Admin Console language to translate phrase
 	 * @return string
 	 * @access public
 	 */
 	public 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
 	 */
 	public function ReplaceLanguageTags($text, $force_escape = null)
 	{
 		return $this->Phrases->ReplaceLanguageTags($text, $force_escape);
 	}
 
 	/**
 	 * Checks if user is logged in, and creates
 	 * user object if so. User object can be recalled
 	 * later using "u.current" prefix_special. Also you may
 	 * get user id by getting "u.current_id" variable.
 	 *
 	 * @return void
 	 * @access protected
 	 */
 	protected function ValidateLogin()
 	{
 		$session = $this->recallObject('Session');
 		/* @var $session Session */
 
 		$user_id = $session->GetField('PortalUserId');
 
 		if ( !$user_id && $user_id != USER_ROOT ) {
 			$user_id = USER_GUEST;
 		}
 
 		$this->SetVar('u.current_id', $user_id);
 
 		if ( !$this->isAdmin ) {
 			// needed for "profile edit", "registration" forms ON FRONT ONLY
 			$this->SetVar('u_id', $user_id);
 		}
 
 		$this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional
 
 		$this->isAdminUser = $this->isAdmin && $this->LoggedIn();
 
 		if ( $this->GetVar('expired') == 1 ) {
 			// this parameter is set only from admin
 			$user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login'));
 			/* @var $user UsersItem */
 
 			$user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired');
 		}
 
 		if ( ($user_id != USER_GUEST) && defined('DBG_REQUREST_LOG') && DBG_REQUREST_LOG ) {
 			$this->HttpQuery->writeRequestLog(DBG_REQUREST_LOG);
 		}
 
 		if ( $user_id != USER_GUEST ) {
 			// normal users + root
 			$this->LoadPersistentVars();
 		}
 
 		$user_timezone = $this->Session->GetField('TimeZone');
 
 		if ( $user_timezone ) {
 			putenv('TZ=' . $user_timezone);
 		}
 	}
 
 	/**
 	 * Loads current user persistent session data
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function LoadPersistentVars()
 	{
 		$this->Session->LoadPersistentVars();
 	}
 
 	/**
 	 * Returns configuration option value by name
 	 *
 	 * @param string $name
 	 * @return string
 	 * @access public
 	 */
 	public function ConfigValue($name)
 	{
 		return $this->cacheManager->ConfigValue($name);
 	}
 
 	/**
 	 * Changes value of individual configuration variable (+resets cache, when needed)
 	 *
 	 * @param string $name
 	 * @param string $value
 	 * @param bool $local_cache_only
 	 * @return string
 	 * @access public
 	 */
 	public function SetConfigValue($name, $value, $local_cache_only = false)
 	{
 		return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only);
 	}
 
 	/**
 	 * Allows to process any type of event
 	 *
 	 * @param kEvent $event
 	 * @param Array $params
 	 * @param Array $specific_params
 	 * @access public
 	 */
 	public function HandleEvent($event, $params = null, $specific_params = null)
 	{
 		if ( isset($params) ) {
 			$event = new kEvent($params, $specific_params);
 		}
 
 		$this->EventManager->HandleEvent($event);
 	}
 
 	/**
 	 * Registers new class in the factory
 	 *
 	 * @param string $real_class Real name of class as in class declaration
 	 * @param string $file Filename in what $real_class is declared
 	 * @param string $pseudo_class Name under this class object will be accessed using getObject method
 	 * @return void
 	 * @access public
 	 */
 	public function registerClass($real_class, $file, $pseudo_class = null)
 	{
 		$this->Factory->registerClass($real_class, $file, $pseudo_class);
 	}
 
 	/**
 	 * Unregisters existing class from factory
 	 *
 	 * @param string $real_class Real name of class as in class declaration
 	 * @param string $pseudo_class Name under this class object is accessed using getObject method
 	 * @return void
 	 * @access public
 	 */
 	public function unregisterClass($real_class, $pseudo_class = null)
 	{
 		$this->Factory->unregisterClass($real_class, $pseudo_class);
 	}
 
 	/**
 	 * Add new scheduled task
 	 *
 	 * @param string $short_name name to be used to store last maintenance run info
 	 * @param string $event_string
 	 * @param int $run_interval run interval in seconds
 	 * @param int $status
 	 * @access public
 	 */
 	public function registerScheduledTask($short_name, $event_string, $run_interval, $status = STATUS_ACTIVE)
 	{
 		$this->EventManager->registerScheduledTask($short_name, $event_string, $run_interval, $status);
 	}
 
 	/**
 	 * Registers Hook from subprefix event to master prefix event
 	 *
 	 * Pattern: Observer
 	 *
 	 * @param string $hook_event
 	 * @param string $do_event
 	 * @param int $mode
 	 * @param bool $conditional
 	 * @access public
 	 */
 	public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false)
 	{
 		$this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional);
 	}
 
 	/**
 	 * Registers build event for given pseudo class
 	 *
 	 * @param string $pseudo_class
 	 * @param string $event_name
 	 * @access public
 	 */
 	public function registerBuildEvent($pseudo_class, $event_name)
 	{
 		$this->EventManager->registerBuildEvent($pseudo_class, $event_name);
 	}
 
 	/**
 	 * Allows one TagProcessor tag act as other TagProcessor tag
 	 *
 	 * @param Array $tag_info
 	 * @return void
 	 * @access public
 	 */
 	public function registerAggregateTag($tag_info)
 	{
 		$aggregator = $this->recallObject('TagsAggregator', 'kArray');
 		/* @var $aggregator kArray */
 
 		$tag_data = Array (
 			$tag_info['LocalPrefix'],
 			$tag_info['LocalTagName'],
 			getArrayValue($tag_info, 'LocalSpecial')
 		);
 
 		$aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data);
 	}
 
 	/**
 	 * Returns object using params specified, creates it if is required
 	 *
 	 * @param string $name
 	 * @param string $pseudo_class
 	 * @param Array $event_params
 	 * @param Array $arguments
 	 * @return kBase
 	 */
 	public function recallObject($name, $pseudo_class = null, $event_params = Array(), $arguments = Array ())
 	{
 		/*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) {
 			// first time, when object with "_prefix_here_" prefix is accessed
 			$this->Debugger->appendTrace();
 		}*/
 
 		return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments);
 	}
 
 	/**
 	 * Returns tag processor for prefix specified
 	 *
 	 * @param string $prefix
 	 * @return kDBTagProcessor
 	 * @access public
 	 */
 	public function recallTagProcessor($prefix)
 	{
 		$this->InitParser(); // because kDBTagProcesor is in NParser dependencies
 
 		return $this->recallObject($prefix . '_TagProcessor');
 	}
 
 	/**
 	 * Checks if object with prefix passes was already created in factory
 	 *
 	 * @param string $name object pseudo_class, prefix
 	 * @return bool
 	 * @access public
 	 */
 	public function hasObject($name)
 	{
 		return $this->Factory->hasObject($name);
 	}
 
 	/**
 	 * Removes object from storage by given name
 	 *
 	 * @param string $name Object's name in the Storage
 	 * @return void
 	 * @access public
 	 */
 	public function removeObject($name)
 	{
 		$this->Factory->DestroyObject($name);
 	}
 
 	/**
 	 * Get's real class name for pseudo class, includes class file and creates class instance
 	 *
 	 * Pattern: Factory Method
 	 *
 	 * @param string $pseudo_class
 	 * @param Array $arguments
 	 * @return kBase
 	 * @access public
 	 */
 	public function makeClass($pseudo_class, $arguments = Array ())
 	{
 		return $this->Factory->makeClass($pseudo_class, $arguments);
 	}
 
 	/**
 	 * Checks if application is in debug mode
 	 *
 	 * @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant
 	 * @return bool
 	 * @author Alex
 	 * @access public
 	 */
 	public function isDebugMode($check_debugger = true)
 	{
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		if ($check_debugger) {
 			$debug_mode = $debug_mode && is_object($this->Debugger);
 		}
 		return $debug_mode;
 	}
 
 	/**
 	 * Apply url rewriting used by mod_rewrite or not
 	 *
 	 * @param bool|null $ssl Force ssl link to be build
 	 * @return bool
 	 * @access public
 	 */
 	public function RewriteURLs($ssl = false)
 	{
 		// case #1,#4:
 		//			we want to create https link from http mode
 		//			we want to create https link from https mode
 		//			conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')
 
 		// case #2,#3:
 		//			we want to create http link from https mode
 		//			we want to create http link from http mode
 		//			conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')
 
 		$allow_rewriting =
 			(!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http
 			|| // or allow rewriting for redirect TO httpS or when already in httpS
 			(($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config!
 
 		return kUtil::constOn('MOD_REWRITE') && $allow_rewriting;
 	}
 
 	/**
 	 * Reads unit (specified by $prefix)
 	 * option specified by $option
 	 *
 	 * @param string $prefix
 	 * @param string $option
 	 * @param mixed $default
 	 * @return string
 	 * @access public
 	 */
 	public function getUnitOption($prefix, $option, $default = false)
 	{
 		return $this->UnitConfigReader->getUnitOption($prefix, $option, $default);
 	}
 
 	/**
 	 * Set's new unit option value
 	 *
 	 * @param string $prefix
 	 * @param string $option
 	 * @param string $value
 	 * @access public
 	 */
 	public function setUnitOption($prefix, $option, $value)
 	{
 		$this->UnitConfigReader->setUnitOption($prefix,$option,$value);
 	}
 
 	/**
 	 * Read all unit with $prefix options
 	 *
 	 * @param string $prefix
 	 * @return Array
 	 * @access public
 	 */
 	public function getUnitOptions($prefix)
 	{
 		return $this->UnitConfigReader->getUnitOptions($prefix);
 	}
 
 	/**
 	 * Returns true if config exists and is allowed for reading
 	 *
 	 * @param string $prefix
 	 * @return bool
 	 */
 	public function prefixRegistred($prefix)
 	{
 		return $this->UnitConfigReader->prefixRegistred($prefix);
 	}
 
 	/**
 	 * Splits any mixing of prefix and
 	 * special into correct ones
 	 *
 	 * @param string $prefix_special
 	 * @return Array
 	 * @access public
 	 */
 	public function processPrefix($prefix_special)
 	{
 		return $this->Factory->processPrefix($prefix_special);
 	}
 
 	/**
 	 * Set's new event for $prefix_special
 	 * passed
 	 *
 	 * @param string $prefix_special
 	 * @param string $event_name
 	 * @return void
 	 * @access public
 	 */
 	public function setEvent($prefix_special, $event_name)
 	{
 		$this->EventManager->setEvent($prefix_special, $event_name);
 	}
 
 	/**
 	 * SQL Error Handler
 	 *
 	 * @param int $code
 	 * @param string $msg
 	 * @param string $sql
 	 * @return bool
 	 * @access public
 	 */
 	public function handleSQLError($code, $msg, $sql)
 	{
 		if ( isset($this->Debugger) ) {
 			$long_error_msg = '<span class="debug_error">' . $msg . ' (' . $code . ')</span><br/><a href="javascript:$Debugger.SetClipboard(\'' . htmlspecialchars($sql) . '\');"><strong>SQL</strong></a>: ' . $this->Debugger->formatSQL($sql);
 			$long_id = $this->Debugger->mapLongError($long_error_msg);
 			$error_msg = mb_substr($msg . ' (' . $code . ') [' . $sql . ']', 0, 1000) . ' #' . $long_id;
 
 			if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) {
 				throw new Exception($error_msg);
 			}
 			else {
 				$this->Debugger->appendTrace();
 			}
 		}
 		else {
 			// when not debug mode, then fatal database query won't break anything
 			$error_msg = '<strong>SQL Error</strong> in sql: ' . $sql . ', code <strong>' . $code . '</strong> (' . $msg . ')';
 		}
 
 		trigger_error($error_msg, E_USER_WARNING);
 
 		return true;
 	}
 
 	/**
 	 * Default error handler
 	 *
 	 * @param int $errno
 	 * @param string $errstr
 	 * @param string $errfile
 	 * @param int $errline
 	 * @param Array $errcontext
 	 * @return bool
 	 * @access public
 	 */
 	public function handleError($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ())
 	{
 		$this->errorLogSilent($errno, $errstr, $errfile, $errline);
 
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
 
 		if ( !$this->errorHandlers || ($debug_mode && $skip_reporting) ) {
 			// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
 			if ( $errno == E_USER_ERROR ) {
 				$this->errorDisplayFatal('<strong>Fatal Error: </strong>' . "{$errstr} in {$errfile} on line {$errline}");
 			}
 
 			if ( !$this->errorHandlers ) {
 				return true;
 			}
 		}
 
 		$res = false;
 		/* @var $handler Closure */
 
 		foreach ($this->errorHandlers as $handler) {
 			if ( is_array($handler) ) {
 				$object =& $handler[0];
 				$method = $handler[1];
 				$res = $object->$method($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 			else {
 				$res = $handler($errno, $errstr, $errfile, $errline, $errcontext);
 			}
 		}
 
 		return $res;
 	}
 
 	/**
 	 * Handles exception
 	 *
 	 * @param Exception $exception
 	 * @return bool
 	 * @access public
 	 */
 	public function handleException($exception)
 	{
 		// transform exception to regular error (no need to rewrite existing error handlers)
 		$errno = $exception->getCode();
 		$errstr = $exception->getMessage();
 		$errfile = $exception->getFile();
 		$errline = $exception->getLine();
 
 		$this->errorLogSilent($errno, $errstr, $errfile, $errline);
 
 		$debug_mode = defined('DEBUG_MODE') && DEBUG_MODE;
 		$skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING;
 
 		if ( !$this->exceptionHandlers || ($debug_mode && $skip_reporting) ) {
 			// when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request)
 			$this->errorDisplayFatal('<strong>' . get_class($exception) . ': </strong>' . "{$errstr} in {$errfile} on line {$errline}");
 
 			if ( !$this->exceptionHandlers ) {
 				return true;
 			}
 		}
 
 		$res = false;
 		/* @var $handler Closure */
 
 		foreach ($this->exceptionHandlers as $handler) {
 			if ( is_array($handler) ) {
 				$object =& $handler[0];
 				$method = $handler[1];
 				$res = $object->$method($exception);
 			}
 			else {
 				$res = $handler($exception);
 			}
 		}
 
 		return $res;
 	}
 
 	/**
 	 * Silently saves each given error message to "silent_log.txt" file, when silent log mode is enabled
 	 * @param int $errno
 	 * @param string $errstr
 	 * @param string $errfile
 	 * @param int $errline
 	 * @return void
 	 * @access protected
 	 */
 	protected function errorLogSilent($errno, $errstr = '', $errfile = '', $errline = null)
 	{
 		if ( !defined('SILENT_LOG') || !SILENT_LOG ) {
 			return;
 		}
 
 		if ( !(defined('DBG_IGNORE_STRICT_ERRORS') && DBG_IGNORE_STRICT_ERRORS && defined('E_STRICT') && ($errno == E_STRICT)) ) {
 			$time = adodb_date('d/m/Y H:i:s');
 
 			$fp = fopen((defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/silent_log.txt', 'a');
 			fwrite($fp, '[' . $time . '] #' . $errno . ': ' . strip_tags($errstr) . ' in [' . $errfile . '] on line ' . $errline . "\n");
 			fclose($fp);
 		}
 	}
 
 	/**
 	 * Displays div with given error message
 	 *
 	 * @param string $msg
 	 * @return void
 	 * @access protected
 	 */
 	protected function errorDisplayFatal($msg)
 	{
 		$margin = $this->isAdmin ? '8px' : 'auto';
 		echo '<div style="background-color: #FEFFBF; margin: ' . $margin . '; padding: 10px; border: 2px solid red; text-align: center">' . $msg . '</div>';
 		exit;
 	}
 
 	/**
 	 * Prints trace, when debug mode is not available
 	 *
 	 * @param bool $return_result
 	 * @param int $skip_levels
 	 * @return string
 	 * @access public
 	 */
 	public function printTrace($return_result = false, $skip_levels = 1)
 	{
 		$ret = Array ();
 		$trace = debug_backtrace(false);
 
 		for ($i = 0; $i < $skip_levels; $i++) {
 			array_shift($trace);
 		}
 
 		foreach ($trace as $level => $trace_info) {
 			if ( isset($trace_info['class']) ) {
 				$object = $trace_info['class'];
 			}
 			elseif ( isset($trace_info['object']) ) {
 				$object = get_class($trace_info['object']);
 			}
 			else {
 				$object = '';
 			}
 
 			$args = '';
 			$type = isset($trace_info['type']) ? $trace_info['type'] : '';
 
 			if ( isset($trace_info['args']) ) {
 				foreach ($trace_info['args'] as $argument) {
 					if ( is_object($argument) ) {
 						$args .= get_class($argument) . ' instance, ';
 					}
 					else {
 						$args .= is_array($argument) ? 'Array' : substr($argument, 0, 10) . ' ..., ';
 					}
 				}
 
 				$args = substr($args, 0, -2);
 			}
 
 			$ret[] = '#' . $level . '  ' . $object . $type . $trace_info['function'] . '(' . $args . ') called at [' . $trace_info['file'] . ':' . $trace_info['line'] . ']';
 		}
 
 		if ( $return_result ) {
 			return implode("\n", $ret);
 		}
 
 		echo implode("\n", $ret);
 
 		return '';
 	}
 
 	/**
 	 * Returns & blocks next ResourceId available in system
 	 *
 	 * @return int
 	 * @access public
 	 */
 	public function NextResourceId()
 	{
 		$table_name = TABLE_PREFIX . 'IdGenerator';
 
 		$this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE');
 		$this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1');
 		$id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name);
 
 		if ( $id === false ) {
 			$this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)');
 			$id = 2;
 		}
 
 		$this->Conn->Query('UNLOCK TABLES');
 
 		return $id - 1;
 	}
 
 	/**
 	 * Returns genealogical main prefix for sub-table prefix passes
 	 * OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix
 	 *
 	 * @param string $current_prefix
 	 * @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not
 	 * @return string
 	 * @access public
 	 */
 	public function GetTopmostPrefix($current_prefix, $real_top = false)
 	{
 		// 1. get genealogical tree of $current_prefix
 		$prefixes = Array ($current_prefix);
 		while ($parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix')) {
 			if ( !$this->prefixRegistred($parent_prefix) ) {
 				// stop searching, when parent prefix is not registered
 				break;
 			}
 
 			$current_prefix = $parent_prefix;
 			array_unshift($prefixes, $current_prefix);
 		}
 
 		if ( $real_top ) {
 			return $current_prefix;
 		}
 
 		// 2. find what if parent is passed
 		$passed = explode(',', $this->GetVar('all_passed'));
 		foreach ($prefixes as $a_prefix) {
 			if ( in_array($a_prefix, $passed) ) {
 				return $a_prefix;
 			}
 		}
 
 		return $current_prefix;
 	}
 
 	/**
 	 * Triggers email event of type Admin
 	 *
 	 * @param string $email_event_name
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 * @access public
 	 */
 	public function EmailEventAdmin($email_event_name, $to_user_id = null, $send_params = Array ())
 	{
 		return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_ADMIN, $to_user_id, $send_params);
 	}
 
 	/**
 	 * Triggers email event of type User
 	 *
 	 * @param string $email_event_name
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 * @access public
 	 */
 	public function EmailEventUser($email_event_name, $to_user_id = null, $send_params = Array ())
 	{
 		return $this->_emailEvent($email_event_name, EmailEvent::EVENT_TYPE_FRONTEND, $to_user_id, $send_params);
 	}
 
 	/**
 	 * Triggers general email event
 	 *
 	 * @param string $email_event_name
 	 * @param int $email_event_type (0 for User, 1 for Admin)
 	 * @param int $to_user_id
 	 * @param array $send_params associative array of direct send params,
 	 *  possible keys: to_email, to_name, from_email, from_name, message, message_text
 	 * @return kEvent
 	 * @access protected
 	 */
 	protected function _emailEvent($email_event_name, $email_event_type, $to_user_id = null, $send_params = Array ())
 	{
 		$email = $this->makeClass('kEmail');
 		/* @var $email kEmail */
 
 		if ( !$email->findEvent($email_event_name, $email_event_type) ) {
 			return false;
 		}
 
 		$email->setParams($send_params);
 
 		return $email->send($to_user_id);
 	}
 
 	/**
 	 * Allows to check if user in this session is logged in or not
 	 *
 	 * @return bool
 	 * @access public
 	 */
 	public 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
 	 * @access public
 	 */
 	public function CheckPermission($name, $type = 1, $cat_id = null)
 	{
 		$perm_helper = $this->recallObject('PermissionsHelper');
 		/* @var $perm_helper kPermissionsHelper */
 
 		return $perm_helper->CheckPermission($name, $type, $cat_id);
 	}
 
 	/**
 	 * Check current admin 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
 	 * @access public
 	 */
 	public function CheckAdminPermission($name, $type = 1, $cat_id = null)
 	{
 		$perm_helper = $this->recallObject('PermissionsHelper');
 		/* @var $perm_helper kPermissionsHelper */
 
 		return $perm_helper->CheckAdminPermission($name, $type, $cat_id);
 	}
 
 	/**
 	 * Set's any field of current visit
 	 *
 	 * @param string $field
 	 * @param mixed $value
 	 * @return void
 	 * @access public
 	 * @todo move to separate module
 	 */
 	public function setVisitField($field, $value)
 	{
 		if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) {
 			// admin logins are not registered 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
 	 * @access public
 	 */
 	public function isInstalled()
 	{
 		return $this->InitDone && (count($this->ModuleInfo) > 0);
 	}
 
 	/**
 	 * Allows to determine if module is installed & enabled
 	 *
 	 * @param string $module_name
 	 * @return bool
 	 * @access public
 	 */
 	public function isModuleEnabled($module_name)
 	{
 		return $this->findModule('Name', $module_name) !== false;
 	}
 
 	/**
 	 * Returns Window ID of passed prefix main prefix (in edit mode)
 	 *
 	 * @param string $prefix
 	 * @return int
 	 * @access public
 	 */
 	public 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
 	 * @access public
 	 */
 	public function GetTempName($table, $wid = '')
 	{
 		return $this->GetTempTablePrefix($wid) . $table;
 	}
 
 	/**
 	 * Builds temporary table prefix based on given window id
 	 *
 	 * @param string $wid
 	 * @return string
 	 * @access public
 	 */
 	public function GetTempTablePrefix($wid = '')
 	{
 		if ( preg_match('/prefix:(.*)/', $wid, $regs) ) {
 			$wid = $this->GetTopmostWid($regs[1]);
 		}
 
 		return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_';
 	}
 
 	/**
 	 * Checks if given table is a temporary table
 	 *
 	 * @param string $table
 	 * @return bool
 	 * @access public
 	 */
 	public function IsTempTable($table)
 	{
 		static $cache = Array ();
 
 		if ( !array_key_exists($table, $cache) ) {
 			$cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table);
 		}
 
 		return (bool)$cache[$table];
 	}
 
 	/**
 	 * Checks, that given prefix is in temp mode
 	 *
 	 * @param string $prefix
 	 * @param string $special
 	 * @return bool
 	 * @access public
 	 */
 	public function IsTempMode($prefix, $special = '')
 	{
 		$top_prefix = $this->GetTopmostPrefix($prefix);
 
 		$var_names = Array (
 			$top_prefix,
 			rtrim($top_prefix . '_' . $special, '_'), // from post
 			rtrim($top_prefix . '.' . $special, '.'), // assembled locally
 		);
 
 		$var_names = array_unique($var_names);
 
 		$temp_mode = false;
 		foreach ($var_names as $var_name) {
 			$value = $this->GetVar($var_name . '_mode');
 
 			if ( $value && (substr($value, 0, 1) == 't') ) {
 				$temp_mode = true;
 				break;
 			}
 		}
 
 		return $temp_mode;
 	}
 
 	/**
 	 * Return live table name based on temp table name
 	 *
 	 * @param string $temp_table
 	 * @return string
 	 */
 	public 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;
 		}
 	}
 
 	/**
 	 * Stops processing of user request and displays given message
 	 *
 	 * @param string $message
 	 * @access public
 	 */
 	public 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;
 	}
 
 	/**
 	 * Returns comma-separated list of groups from given user
 	 *
 	 * @param int $user_id
 	 * @return string
 	 */
 	public function getUserGroups($user_id)
 	{
 		switch ($user_id) {
 			case USER_ROOT:
 				$user_groups = $this->ConfigValue('User_LoggedInGroup');
 				break;
 
 			case USER_GUEST:
 				$user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup');
 				break;
 
 			default:
 				$sql = 'SELECT GroupId
 						FROM ' . TABLE_PREFIX . 'UserGroupRelations
 						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 scheduled_tasks supported)
 	 *
 	 * @return bool
 	 * @access public
 	 */
 	/*public 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 presence in database
 	 *
 	 * @param string $table_name
 	 * @param bool $force
 	 * @return bool
 	 * @access public
 	 */
 	public function TableFound($table_name, $force = false)
 	{
 		return $this->Conn->TableFound($table_name, $force);
 	}
 
 	/**
 	 * 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 parameters)
 	 * @param bool $multiple_results
 	 * @return mixed
 	 * @access public
 	 */
 	public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false)
 	{
 		$count_helper = $this->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		return $count_helper->getCounter($name, $params, $query_name, $multiple_results);
 	}
 
 	/**
 	 * Resets counter, which are affected by one of specified tables
 	 *
 	 * @param string $tables comma separated tables list used in counting sqls
 	 * @return void
 	 * @access public
 	 */
 	public function resetCounters($tables)
 	{
 		if ( kUtil::constOn('IS_INSTALL') ) {
 			return;
 		}
 
 		$count_helper = $this->recallObject('CountHelper');
 		/* @var $count_helper kCountHelper */
 
 		$count_helper->resetCounters($tables);
 	}
 
 	/**
 	 * Sends XML header + optionally displays xml heading
 	 *
 	 * @param string|bool $xml_version
 	 * @return string
 	 * @access public
 	 * @author Alex
 	 */
 	public function XMLHeader($xml_version = false)
 	{
 		$lang = $this->recallObject('lang.current');
 		/* @var $lang LanguagesItem */
 
 		$this->setContentType('text/xml');
 
 		return $xml_version ? '<?xml version="' . $xml_version . '" encoding="' . $lang->GetDBField('Charset') . '"?>' : '';
 	}
 
 	/**
 	 * Returns category tree
 	 *
 	 * @param int $category_id
 	 * @return Array
 	 * @access public
 	 */
 	public function getTreeIndex($category_id)
 	{
 		$tree_index = $this->getCategoryCache($category_id, 'category_tree');
 
 		if ( $tree_index ) {
 			$ret = Array ();
 			list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index);
 
 			return $ret;
 		}
 
 		return false;
 	}
 
 	/**
 	 * Base category of all categories
 	 * Usually replaced category, with ID = 0 in category-related operations.
 	 *
 	 * @return int
 	 * @access public
 	 */
 	public function getBaseCategory()
 	{
 		// same, what $this->findModule('Name', 'Core', 'RootCat') does
 		// don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade
 
 		return $this->ModuleInfo['Core']['RootCat'];
 	}
 
 	/**
 	 * Deletes all data, that was cached during unit config parsing (excluding unit config locations)
 	 *
 	 * @param Array $config_variables
 	 * @access public
 	 */
 	public function DeleteUnitCache($config_variables = null)
 	{
 		$this->cacheManager->DeleteUnitCache($config_variables);
 	}
 
 	/**
 	 * Deletes cached section tree, used during permission checking and admin console tree display
 	 *
 	 * @return void
 	 * @access public
 	 */
 	public function DeleteSectionCache()
 	{
 		$this->cacheManager->DeleteSectionCache();
 	}
 
 	/**
 	 * Sets data from cache to object
 	 *
 	 * @param Array $data
 	 * @access public
 	 */
 	public function setFromCache(&$data)
 	{
 		$this->Factory->setFromCache($data);
 		$this->UnitConfigReader->setFromCache($data);
 		$this->EventManager->setFromCache($data);
 
 		$this->ReplacementTemplates = $data['Application.ReplacementTemplates'];
 		$this->RewriteListeners = $data['Application.RewriteListeners'];
 		$this->ModuleInfo = $data['Application.ModuleInfo'];
 	}
 
 	/**
 	 * Gets object data for caching
 	 * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc)
 	 *
 	 * @access public
 	 * @return Array
 	 */
 	public function getToCache()
 	{
 		return array_merge(
 			$this->Factory->getToCache(),
 			$this->UnitConfigReader->getToCache(),
 			$this->EventManager->getToCache(),
 			Array (
 				'Application.ReplacementTemplates' => $this->ReplacementTemplates,
 				'Application.RewriteListeners' => $this->RewriteListeners,
 				'Application.ModuleInfo' => $this->ModuleInfo,
 			)
 		);
 	}
 
 	public function delayUnitProcessing($method, $params)
 	{
 		$this->cacheManager->delayUnitProcessing($method, $params);
 	}
 
 	/**
 	 * Returns current maintenance mode state
 	 *
 	 * @param bool $check_ips
 	 * @return int
 	 * @access public
 	 */
 	public function getMaintenanceMode($check_ips = true)
 	{
 		$exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : '';
 		$setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT';
 
 		if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) {
 			$exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false;
 
 			if ( !$exception_ip ) {
 				return constant($setting_name);
 			}
 		}
 
 		return MaintenanceMode::NONE;
 	}
 
 	/**
 	 * Sets content type of the page
 	 *
 	 * @param string $content_type
 	 * @param bool $include_charset
 	 * @return void
 	 * @access public
 	 */
 	public function setContentType($content_type = 'text/html', $include_charset = null)
 	{
 		static $aleady_set = false;
 
 		if ( $aleady_set ) {
 			return;
 		}
 
 		$header = 'Content-type: ' . $content_type;
 
 		if ( !isset($include_charset) ) {
 			$include_charset = $content_type = 'text/html' || $content_type = 'text/xml';
 		}
 
 		if ( $include_charset ) {
 			$language = $this->recallObject('lang.current');
 			/* @var $language LanguagesItem */
 
 			$header .= '; charset=' . $language->GetDBField('Charset');
 		}
 
 		$aleady_set = true;
 		header($header);
 	}
 }
\ No newline at end of file
Index: branches/5.2.x/core/units/helpers/language_import_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/language_import_helper.php	(revision 15238)
+++ branches/5.2.x/core/units/helpers/language_import_helper.php	(revision 15239)
@@ -1,1222 +1,1218 @@
 <?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.
 */
 
 /**
  * Language pack format version description
  *
  * v1
  * ==========
  * All language properties are separate nodes inside <LANGUAGE> node. There are
  * two more nodes PHRASES and EVENTS for phrase and email event translations.
  *
  * v2
  * ==========
  * All data, that will end up in Language table is now attributes of LANGUAGE node
  * and is name exactly as field name, that will be used to store that data.
  *
  * v4
  * ==========
  * Hint & Column translation added to each phrase translation
  *
  * v5
  * ==========
  * Use separate xml nodes for subject, headers, html & plain translations
  *
  * v6
  * ==========
  * Added e-mail design templates
  *
  */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	define('LANG_OVERWRITE_EXISTING', 1);
 	define('LANG_SKIP_EXISTING', 2);
 
 	class LanguageImportHelper extends kHelper {
 
 		/**
 		 * Current Language in import
 		 *
 		 * @var LanguagesItem
 		 */
 		var $lang_object = null;
 
 		/**
 		 * Current user's IP address
 		 *
 		 * @var string
 		 */
 		var $ip_address = '';
 
 		/**
 		 * Event type + name mapping to id (from system)
 		 *
 		 * @var Array
 		 */
 		var $events_hash = Array ();
 
 		/**
 		 * Language pack import mode
 		 *
 		 * @var int
 		 */
 		var $import_mode = LANG_SKIP_EXISTING;
 
 		/**
 		 * Language IDs, that were imported
 		 *
 		 * @var Array
 		 */
 		var $_languages = Array ();
 
 		/**
 		 * Temporary table names to perform import on
 		 *
 		 * @var Array
 		 */
 		var $_tables = Array ();
 
 		/**
 		 * Phrase types allowed for import/export operations
 		 *
 		 * @var Array
 		 */
 		var $phrase_types_allowed = Array ();
 
 		/**
 		 * Encoding, used for language pack exporting
 		 *
 		 * @var string
 		 */
 		var $_exportEncoding = 'base64';
 
 		/**
 		 * Exported data limits (all or only specified ones)
 		 *
 		 * @var Array
 		 */
 		var $_exportLimits = Array (
 			'phrases' => false,
 			'emailevents' => false,
 			'country-state' => false,
 		);
 
 		/**
 		 * Debug language pack import process
 		 *
 		 * @var bool
 		 */
 		var $_debugMode = false;
 
 		/**
 		 * Latest version of language pack format. Versions are not backwards compatible!
 		 *
 		 * @var int
 		 */
 		var $_latestVersion = 6;
 
 		/**
 		 * Prefix-based serial numbers, that should be changed after import is finished
 		 *
 		 * @var Array
 		 */
 		var $changedPrefixes = Array ();
 
 		public function __construct()
 		{
 			parent::__construct();
 
 			// "core/install/english.lang", phrase count: 3318, xml parse time on windows: 10s, insert time: 0.058s
 			set_time_limit(0);
 			ini_set('memory_limit', -1);
 
 			$this->lang_object = $this->Application->recallObject('lang.import', null, Array ('skip_autoload' => true));
 
 			if (!(defined('IS_INSTALL') && IS_INSTALL)) {
 				// perform only, when not in installation mode
 				$this->_updateEventsCache();
 			}
 
 			$this->ip_address = getenv('HTTP_X_FORWARDED_FOR') ? getenv('HTTP_X_FORWARDED_FOR') : getenv('REMOTE_ADDR');
 
 //			$this->_debugMode = $this->Application->isDebugMode();
 		}
 
 		/**
 		 * Performs import of given language pack (former Parse method)
 		 *
 		 * @param string $filename
 		 * @param string $phrase_types
 		 * @param Array $module_ids
 		 * @param int $import_mode
 		 * @return bool
 		 */
 		function performImport($filename, $phrase_types, $module_ids, $import_mode = LANG_SKIP_EXISTING)
 		{
 			// define the XML parsing routines/functions to call based on the handler path
 			if (!file_exists($filename) || !$phrase_types /*|| !$module_ids*/) {
 				return false;
 			}
 
 			if ($this->_debugMode) {
 				$start_time = microtime(true);
 				$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")');
 			}
 
 			if (defined('IS_INSTALL') && IS_INSTALL) {
 				// new events could be added during module upgrade
 				$this->_updateEventsCache();
 			}
 
 			$this->_initImportTables();
 
 			$phrase_types = explode('|', substr($phrase_types, 1, -1) );
 //			$module_ids = explode('|', substr($module_ids, 1, -1) );
 
 			$this->phrase_types_allowed = array_flip($phrase_types);
 			$this->import_mode = $import_mode;
 
 			$this->_parseXML($filename);
 
 			// copy data from temp tables to live
 			foreach ($this->_languages as $language_id) {
 				$this->_performUpgrade($language_id, 'phrases', 'PhraseKey', Array ('l%s_Translation', 'l%s_HintTranslation', 'l%s_ColumnTranslation', 'PhraseType'));
 				$this->_performUpgrade($language_id, 'emailevents', 'EventId', Array ('l%s_Subject', 'Headers', 'l%s_HtmlBody', 'l%s_PlainTextBody'));
 				$this->_performUpgrade($language_id, 'country-state', 'CountryStateId', Array ('l%s_Name'));
 			}
 
 			$this->_initImportTables(true);
 			$this->changedPrefixes = array_unique($this->changedPrefixes);
 
 			foreach ($this->changedPrefixes as $prefix) {
 				$this->Application->incrementCacheSerial($prefix);
 			}
 
 			if ($this->_debugMode) {
 				$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time));
 			}
 
 			return true;
 		}
 
 		/**
 		 * Creates XML file with exported language data (former Create method)
 		 *
 		 * @param string $filename filename to export into
 		 * @param Array $phrase_types phrases types to export from modules passed in $module_ids
 		 * @param Array $language_ids IDs of languages to export
 		 * @param Array $module_ids IDs of modules to export phrases from
 		 */
 		function performExport($filename, $phrase_types, $language_ids, $module_ids)
 		{
 			$fp = fopen($filename,'w');
 			if (!$fp || !$phrase_types || !$module_ids || !$language_ids) {
 				return false;
 			}
 
 			$phrase_types = explode('|', substr($phrase_types, 1, -1) );
 			$module_ids = explode('|', substr($module_ids, 1, -1) );
 
 			$ret = '<LANGUAGES Version="' . $this->_latestVersion . '">' . "\n";
 
 			$export_fields = $this->_getExportFields();
 
 			// get languages
 			$sql = 'SELECT *
 					FROM ' . $this->Application->getUnitOption('lang','TableName') . '
 					WHERE LanguageId IN (' . implode(',', $language_ids) . ')';
 			$languages = $this->Conn->Query($sql, 'LanguageId');
 
 			// get phrases
 			$phrase_modules = $module_ids;
 			array_push($phrase_modules, ''); // for old language packs without module
 
 			$phrase_modules = $this->Conn->qstrArray($phrase_modules);
 
 			// apply phrase selection limit
 			if ($this->_exportLimits['phrases']) {
 				$escaped_phrases = $this->Conn->qstrArray($this->_exportLimits['phrases']);
 				$limit_where = 'Phrase IN (' . implode(',', $escaped_phrases) . ')';
 			}
 			else {
 				$limit_where = 'TRUE';
 			}
 
 			$sql = 'SELECT *
 					FROM ' . $this->Application->getUnitOption('phrases','TableName') . '
 					WHERE PhraseType IN (' . implode(',', $phrase_types) . ') AND Module IN (' . implode(',', $phrase_modules) . ') AND ' . $limit_where . '
 					ORDER BY Phrase';
 			$phrases = $this->Conn->Query($sql, 'PhraseId');
 
 			// email events
 			$module_sql = preg_replace('/(.*),/U', 'INSTR(Module,\'\\1\') OR ', implode(',', $module_ids) . ',');
 
 			// apply event selection limit
 			if ($this->_exportLimits['emailevents']) {
 				$escaped_email_events = $this->Conn->qstrArray($this->_exportLimits['emailevents']);
 				$limit_where = '`Event` IN (' . implode(',', $escaped_email_events) . ')';
 			}
 			else {
 				$limit_where = 'TRUE';
 			}
 
 			$sql = 'SELECT *
 					FROM ' . $this->Application->getUnitOption('emailevents', 'TableName') . '
 					WHERE `Type` IN (' . implode(',', $phrase_types) . ') AND (' . substr($module_sql, 0, -4) . ') AND ' . $limit_where . '
 					ORDER BY `Event`, `Type`';
 			$events = $this->Conn->Query($sql, 'EventId');
 
 			if ( in_array('Core', $module_ids) ) {
 				if ($this->_exportLimits['country-state']) {
 					$escaped_countries = $this->Conn->qstrArray($this->_exportLimits['country-state']);
 					$limit_where = '`IsoCode` IN (' . implode(',', $escaped_countries) . ')';
 				}
 				else {
 					$limit_where = 'TRUE';
 				}
 
 				$country_table = $this->Application->getUnitOption('country-state', 'TableName');
 
 				// countries
 				$sql = 'SELECT *
 						FROM ' . $country_table . '
 						WHERE Type = ' . DESTINATION_TYPE_COUNTRY . ' AND ' . $limit_where . '
 						ORDER BY `IsoCode`';
 				$countries = $this->Conn->Query($sql, 'CountryStateId');
 
 				// states
 				$sql = 'SELECT state.*
 						FROM ' . $country_table . ' state
 						JOIN ' . $country_table . ' country ON country.CountryStateId = state.StateCountryId
 						WHERE state.Type = ' . DESTINATION_TYPE_STATE . ' AND ' . str_replace('`IsoCode`', 'country.`IsoCode`', $limit_where) . '
 						ORDER BY state.`IsoCode`';
 				$states = $this->Conn->Query($sql, 'CountryStateId');
 
 				foreach ($states as $state_id => $state_data) {
 					$country_id = $state_data['StateCountryId'];
 
 					if ( !array_key_exists('States', $countries[$country_id]) ) {
 						$countries[$country_id]['States'] = Array ();
 					}
 
 					$countries[$country_id]['States'][] = $state_id;
 				}
 			}
 
 			foreach ($languages as $language_id => $language_info) {
 				// language
 				$ret .= "\t" . '<LANGUAGE Encoding="' . $this->_exportEncoding . '"';
 
 				foreach ($export_fields	as $export_field) {
 					$ret .= ' ' . $export_field . '="' . htmlspecialchars($language_info[$export_field]) . '"';
 				}
 
 				$ret .= '>' . "\n";
 
 				// filename replacements
 				$replacements = $language_info['FilenameReplacements'];
 
 				if ( $replacements ) {
 					$ret .= "\t\t" . '<REPLACEMENTS>' . $this->_exportConvert($replacements) . '</REPLACEMENTS>' . "\n";
 				}
 
 				// e-mail design templates
 				if ( $language_info['HtmlEmailTemplate'] || $language_info['TextEmailTemplate'] ) {
 					$ret .= "\t\t" . '<EMAILDESIGNS>' . "\n";
 
 					if ( $language_info['HtmlEmailTemplate'] ) {
 						$ret .= "\t\t\t" . '<HTML>' . $this->_exportConvert($language_info['HtmlEmailTemplate']) . '</HTML>' . "\n";
 					}
 
 					if ( $language_info['TextEmailTemplate'] ) {
 						$ret .= "\t\t\t" . '<TEXT>' . $this->_exportConvert($language_info['TextEmailTemplate']) . '</TEXT>' . "\n";
 					}
 
 					$ret .= "\t\t" . '</EMAILDESIGNS>' . "\n";
 				}
 
 				// phrases
 				if ($phrases) {
 					$ret .= "\t\t" . '<PHRASES>' . "\n";
 					foreach ($phrases as $phrase_id => $phrase) {
 						$translation = $phrase['l' . $language_id . '_Translation'];
 						$hint_translation = $phrase['l' . $language_id . '_HintTranslation'];
 						$column_translation = $phrase['l' . $language_id . '_ColumnTranslation'];
 
 						if (!$translation) {
 							// phrase is not translated on given language
 							continue;
 						}
 
 						if ( $this->_exportEncoding == 'base64' ) {
 							$hint_translation = base64_encode($hint_translation);
 							$column_translation = base64_encode($column_translation);
 						}
 						else {
 							$hint_translation = htmlspecialchars($hint_translation);
 							$column_translation = htmlspecialchars($column_translation);
 						}
 
 						$attributes = Array (
 							'Label="' . $phrase['Phrase'] . '"',
 							'Module="' . $phrase['Module'] . '"',
 							'Type="' . $phrase['PhraseType'] . '"'
 						);
 
 						if ( $phrase['l' . $language_id . '_HintTranslation'] ) {
 							$attributes[] = 'Hint="' . $hint_translation . '"';
 						}
 
 						if ( $phrase['l' . $language_id . '_ColumnTranslation'] ) {
 							$attributes[] = 'Column="' . $column_translation . '"';
 						}
 
 						$ret .= "\t\t\t" . '<PHRASE ' . implode(' ', $attributes) . '>' . $this->_exportConvert($translation) . '</PHRASE>' . "\n";
 					}
 
 					$ret .= "\t\t" . '</PHRASES>' . "\n";
 				}
 
 				// email events
 				if ($events) {
 					$ret .= "\t\t" . '<EVENTS>' . "\n";
 
 					foreach ($events as $event_data) {
 						$fields_hash = Array (
 							'HEADERS' => $event_data['Headers'],
 							'SUBJECT' => $event_data['l' . $language_id . '_Subject'],
 							'HTMLBODY' => $event_data['l' . $language_id . '_HtmlBody'],
 							'PLAINTEXTBODY' => $event_data['l' . $language_id . '_PlainTextBody'],
 						);
 
 						$data = '';
 
 						foreach ($fields_hash as $xml_node => $xml_content) {
 							if ( $xml_content ) {
 								$data .= "\t\t\t\t" . '<' . $xml_node . '>' . $this->_exportConvert($xml_content) . '</' . $xml_node . '>' . "\n";
 							}
 						}
 
 						if ( $data ) {
 							$ret .= "\t\t\t" . '<EVENT Event="' . $event_data['Event'] . '" Type="' . $event_data['Type'] . '">' . "\n" . $data . "\t\t\t" . '</EVENT>' . "\n";
 						}
 					}
 
 					$ret .= "\t\t" . '</EVENTS>' . "\n";
 				}
 
 				if (in_array('Core', $module_ids) && $countries) {
 					$ret .= "\t\t" . '<COUNTRIES>' . "\n";
 					foreach ($countries as $country_data) {
 						$translation = $country_data['l' . $language_id . '_Name'];
 
 						if (!$translation) {
 							// country is not translated on given language
 							continue;
 						}
 
 						$data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation;
 
 						if (array_key_exists('States', $country_data)) {
 							$ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '">' . "\n";
 
 							foreach ($country_data['States'] as $state_id) {
 								$translation = $states[$state_id]['l' . $language_id . '_Name'];
 
 								if (!$translation) {
 									// state is not translated on given language
 									continue;
 								}
 
 								$data = $this->_exportEncoding == 'base64' ? base64_encode($translation) : $translation;
 								$ret .= "\t\t\t\t" . '<STATE Iso="' . $states[$state_id]['IsoCode'] . '" Translation="' . $data . '"/>' . "\n";
 							}
 
 							$ret  .= "\t\t\t" . '</COUNTRY>' . "\n";
 						}
 						else {
 							$ret .= "\t\t\t" . '<COUNTRY Iso="' . $country_data['IsoCode'] . '" Translation="' . $data . '"/>' . "\n";
 						}
 					}
 
 					$ret .= "\t\t" . '</COUNTRIES>' . "\n";
 				}
 
 				$ret .= "\t" . '</LANGUAGE>' . "\n";
 			}
 
 			$ret .= '</LANGUAGES>';
 			fwrite($fp, $ret);
 			fclose($fp);
 
 			return true;
 		}
 
 		/**
 		 * Converts string before placing into export file
 		 *
 		 * @param string $string
 		 * @return string
 		 * @access protected
 		 */
 		protected function _exportConvert($string)
 		{
 			return $this->_exportEncoding == 'base64' ? base64_encode($string) : '<![CDATA[' . $string . ']]>';
 		}
 
 		/**
 		 * Sets language pack encoding (not charset) used during export
 		 *
 		 * @param string $encoding
 		 */
 		function setExportEncoding($encoding)
 		{
 			$this->_exportEncoding = $encoding;
 		}
 
 		/**
 		 * Sets language pack data limit for export
 		 *
 		 * @param string $prefix
 		 * @param string $data
 		 */
 		function setExportLimit($prefix, $data = null)
 		{
 			if ( !isset($data) ) {
 				$key_field = $prefix == 'phrases' ? 'Phrase' : 'Event';
 				$ids = $this->getExportIDs($prefix);
 
 				$sql = 'SELECT ' . $key_field . '
 						FROM ' . $this->Application->getUnitOption($prefix, 'TableName') . '
 						WHERE ' . $this->Application->getUnitOption($prefix, 'IDField') . ' IN (' . $ids . ')';
-				$rs = $this->Conn->QueryRaw($sql);
+				$rows = $this->Conn->GetIterator($sql);
 
-				if ( $this->Conn->RowCount($rs) ) {
+				if ( count($rows) ) {
 					$data = '';
 
-					while ( ($row = $this->Conn->GetNextRow($rs)) ) {
+					foreach ($rows as $row) {
 						$data .= ',' . $row[$key_field];
 					}
 
 					$data = substr($data, 1);
 				}
-
-				$this->Conn->Destroy($rs);
 			}
 
 			if ( !is_array($data) ) {
 				$data = str_replace(',', "\n", $data);
 				$data = preg_replace("/\n+/", "\n", str_replace("\r", '', trim($data)));
 				$data = $data ? array_map('trim', explode("\n", $data)) : Array ();
 			}
 
 			$this->_exportLimits[$prefix] = $data;
 		}
 
 		/**
 		 * Performs upgrade of given language pack part
 		 *
 		 * @param int $language_id
 		 * @param string $prefix
 		 * @param string $unique_field
 		 * @param Array $data_fields
 		 */
 		function _performUpgrade($language_id, $prefix, $unique_field, $data_fields)
 		{
 			$live_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], false);
 			$temp_records = $this->_getTableData($language_id, $prefix, $unique_field, $data_fields[0], true);
 
 			if (!$temp_records) {
 				// no data for given language
 				return ;
 			}
 
 			// perform insert for records, that are missing in live table
 			$to_insert = array_diff($temp_records, $live_records);
 
 			if ($to_insert) {
 				$to_insert = $this->Conn->qstrArray($to_insert);
 
 				$sql = 'INSERT INTO ' . $this->Application->getUnitOption($prefix, 'TableName') . '
 						SELECT *
 						FROM ' . $this->_tables[$prefix] . '
 						WHERE ' . $unique_field . ' IN (' . implode(',', $to_insert) . ')';
 				$this->Conn->Query($sql);
 
 				// new records were added
 				$this->changedPrefixes[] = $prefix;
 			}
 
 			// perform update for records, that are present in live table
 			$to_update = array_diff($temp_records, $to_insert);
 
 			if ($to_update) {
 				$to_update = $this->Conn->qstrArray($to_update);
 
 				$sql = 'UPDATE ' . $this->Application->getUnitOption($prefix, 'TableName') . ' live
 						SET ';
 
 				foreach ($data_fields as $index => $data_field) {
 					$data_field = sprintf($data_field, $language_id);
 
 					$sql .= '	live.' . $data_field . ' = (
 									SELECT temp' . $index . '.' . $data_field . '
 									FROM ' . $this->_tables[$prefix] . ' temp' . $index . '
 									WHERE temp' . $index . '.' . $unique_field . ' = live.' . $unique_field . '
 								),';
 				}
 
 				$sql = substr($sql, 0, -1); // cut last comma
 
 				$where_clause = Array (
 					// this won't make any difference, but just in case
 					$unique_field . ' IN (' . implode(',', $to_update) . ')',
 				);
 
 				if ($this->import_mode == LANG_SKIP_EXISTING) {
 					// empty OR not set
 					$data_field = sprintf($data_fields[0], $language_id);
 					$where_clause[] = '(' . $data_field . ' = "") OR (' . $data_field . ' IS NULL)';
 				}
 
 				if ($where_clause) {
 					$sql .= "\n" . 'WHERE (' . implode(') AND (', $where_clause) . ')';
 				}
 
 				$this->Conn->Query($sql);
 
 				if ($this->Conn->getAffectedRows() > 0) {
 					// existing records were updated
 					$this->changedPrefixes[] = $prefix;
 				}
 			}
 		}
 
 		/**
 		 * Returns data from given table used for language pack upgrade
 		 *
 		 * @param int $language_id
 		 * @param string $prefix
 		 * @param string $unique_field
 		 * @param string $data_field
 		 * @param bool $temp_mode
 		 * @return Array
 		 */
 		function _getTableData($language_id, $prefix, $unique_field, $data_field, $temp_mode = false)
 		{
 			$data_field = sprintf($data_field, $language_id);
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 
 			if ($temp_mode) {
 				// for temp table get only records, that have contents on given language (not empty and isset)
 				$sql = 'SELECT ' . $unique_field . '
 						FROM ' . $this->Application->GetTempName($table_name, 'prefix:' . $prefix) . '
 						WHERE (' . $data_field . ' <> "") AND (' . $data_field . ' IS NOT NULL)';
 			}
 			else {
 				// for live table get all records, no matter on what language
 				$sql = 'SELECT ' . $unique_field . '
 						FROM ' . $table_name;
 			}
 
 			return $this->Conn->GetCol($sql);
 		}
 
 		function _parseXML($filename)
 		{
 			if ( $this->_debugMode ) {
 				$start_time = microtime(true);
 				$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '")');
 			}
 
 			$languages = simplexml_load_file($filename);
 
 			if ( $languages === false) {
 				// invalid language pack contents
 				return false;
 			}
 
 			// PHP 5.3 version would be: $languages->count()
 			if ( count($languages->children()) ) {
 				$this->_processLanguages($languages);
 			}
 
 			if ( $this->_debugMode ) {
 				$this->Application->Debugger->appendHTML(__CLASS__ . '::' . __FUNCTION__ . '("' . $filename . '"): ' . (microtime(true) - $start_time));
 			}
 
 			return true;
 		}
 
 		/**
 		 * Creates temporary tables, used during language import
 		 *
 		 * @param bool $drop_only
 		 */
 		function _initImportTables($drop_only = false)
 		{
 			$this->_tables['phrases'] = $this->_prepareTempTable('phrases', $drop_only);
 			$this->_tables['emailevents'] = $this->_prepareTempTable('emailevents', $drop_only);
 			$this->_tables['country-state'] = $this->_prepareTempTable('country-state', $drop_only);
 		}
 
 		/**
 		 * Create temp table for prefix, if table already exists, then delete it and create again
 		 *
 		 * @param string $prefix
 		 * @param bool $drop_only
 		 * @return string Name of created temp table
 		 * @access protected
 		 */
 		protected function _prepareTempTable($prefix, $drop_only = false)
 		{
 			$id_field = $this->Application->getUnitOption($prefix, 'IDField');
 			$table = $this->Application->getUnitOption($prefix,'TableName');
 			$temp_table = $this->Application->GetTempName($table);
 
 			$sql = 'DROP TABLE IF EXISTS %s';
 			$this->Conn->Query( sprintf($sql, $temp_table) );
 
 			if (!$drop_only) {
 				$sql = 'CREATE TABLE ' . $temp_table . ' SELECT * FROM ' . $table . ' WHERE 0';
 				$this->Conn->Query($sql);
 
 				$sql = 'ALTER TABLE %1$s CHANGE %2$s %2$s INT(11) NOT NULL DEFAULT "0"';
 				$this->Conn->Query( sprintf($sql, $temp_table, $id_field) );
 
 				switch ($prefix) {
 					case 'phrases':
 						$unique_field = 'PhraseKey';
 						break;
 
 					case 'emailevents':
 						$unique_field = 'EventId';
 						break;
 
 					case 'country-state':
 						$unique_field = 'CountryStateId';
 						break;
 
 					default:
 						throw new Exception('Unknown prefix "<strong>' . $prefix . '</strong>" during language pack import');
 						break;
 				}
 
 				$sql = 'ALTER TABLE ' . $temp_table . ' ADD UNIQUE (' . $unique_field . ')';
 				$this->Conn->Query($sql);
 			}
 
 			return $temp_table;
 		}
 
 		/**
 		 * Prepares mapping between event name+type and their ids in database
 		 *
 		 */
 		function _updateEventsCache()
 		{
 			$sql = 'SELECT EventId, CONCAT(Event,"_",Type) AS EventMix
 					FROM ' . TABLE_PREFIX . 'EmailEvents';
 			$this->events_hash = $this->Conn->GetCol($sql, 'EventMix');
 		}
 
 		/**
 		 * Returns language fields to be exported
 		 *
 		 * @return Array
 		 */
 		function _getExportFields()
 		{
 			return Array (
 				'PackName', 'LocalName', 'DateFormat', 'TimeFormat', 'InputDateFormat', 'InputTimeFormat',
 				'DecimalPoint', 'ThousandSep', 'Charset', 'UnitSystem', 'Locale', 'UserDocsUrl'
 			);
 		}
 
 		/**
 		 * Processes parsed XML
 		 *
 		 * @param SimpleXMLElement $languages
 		 */
 		function _processLanguages($languages)
 		{
 			$version = (int)$languages['Version'];
 
 			if ( !$version ) {
 				// version missing -> guess it
 				if ( $languages->DATEFORMAT->getName() ) {
 					$version = 1;
 				}
 				elseif ( (string)$languages->LANGUAGE['Charset'] != '' ) {
 					$version = 2;
 				}
 			}
 
 			if ( $version == 1 ) {
 				$field_mapping = Array (
 					'DATEFORMAT' => 'DateFormat',
 					'TIMEFORMAT' => 'TimeFormat',
 					'INPUTDATEFORMAT' => 'InputDateFormat',
 					'INPUTTIMEFORMAT' => 'InputTimeFormat',
 					'DECIMAL' => 'DecimalPoint',
 					'THOUSANDS' => 'ThousandSep',
 					'CHARSET' => 'Charset',
 					'UNITSYSTEM' => 'UnitSystem',
 					'DOCS_URL' => 'UserDocsUrl',
 				);
 			}
 			else {
 				$export_fields = $this->_getExportFields();
 			}
 
 			foreach ($languages as $language_node) {
 				$language_id = false;
 
 				$fields_hash = Array (
 					'PackName' => (string)$language_node['PackName'],
 					'LocalName' => (string)$language_node['PackName'],
 					'Encoding' => (string)$language_node['Encoding'],
 					'Charset' => 'utf-8',
 					'SynchronizationModes' => Language::SYNCHRONIZE_DEFAULT,
 				);
 
 				if ( $version > 1 ) {
 					foreach ($export_fields as $export_field) {
 						if ( (string)$language_node[$export_field] ) {
 							$fields_hash[$export_field] = (string)$language_node[$export_field];
 						}
 					}
 				}
 
 				$container_nodes = Array ('PHRASES', 'EVENTS', 'COUNTRIES');
 
 				foreach ($language_node as $sub_node) {
 					/* @var $sub_node SimpleXMLElement */
 
 					if ( in_array($sub_node->getName(), $container_nodes) ) {
 						// PHP 5.3 version would be: !$sub_node->count()
 						if ( !count($sub_node->children()) ) {
 							continue;
 						}
 
 						if ( !$language_id ) {
 							$language_id = $this->_processLanguage($fields_hash);
 						}
 					}
 
 					switch ($sub_node->getName()) {
 						case 'PHRASES':
 							$this->_processPhrases($sub_node, $language_id, $fields_hash['Encoding']);
 							break;
 
 						case 'EVENTS':
 							$this->_processEvents($sub_node, $language_id, $fields_hash['Encoding']);
 							break;
 
 						case 'COUNTRIES':
 							$this->_processCountries($sub_node, $language_id, $fields_hash['Encoding']);
 							break;
 
 						case 'REPLACEMENTS':
 							// added since v2
 							$replacements = (string)$sub_node;
 
 							if ( $fields_hash['Encoding'] != 'plain' ) {
 								$replacements = base64_decode($replacements);
 							}
 
 							$fields_hash['FilenameReplacements'] = $replacements;
 							break;
 
 						case 'EMAILDESIGNS':
 							// added since v6
 							$this->_decodeEmailDesignTemplate($fields_hash, 'HtmlEmailTemplate', (string)$sub_node->HTML);
 							$this->_decodeEmailDesignTemplate($fields_hash, 'TextEmailTemplate', (string)$sub_node->TEXT);
 							break;
 
 						default:
 							if ( $version == 1 ) {
 								$fields_hash[$field_mapping[$sub_node->Name]] = (string)$sub_node;
 							}
 							break;
 					}
 				}
 			}
 		}
 
 		/**
 		 * Decodes e-mail template design from language pack
 		 *
 		 * @param Array $fields_hash
 		 * @param string $field
 		 * @param string $design_template
 		 */
 		protected function _decodeEmailDesignTemplate(&$fields_hash, $field, $design_template)
 		{
 			if ( $fields_hash['Encoding'] != 'plain' ) {
 				$design_template = base64_decode($design_template);
 			}
 
 			if ( $design_template ) {
 				$fields_hash[$field] = $design_template;
 			}
 		}
 
 		/**
 		 * Performs phases import
 		 *
 		 * @param SimpleXMLElement $phrases
 		 * @param int $language_id
 		 * @param string $language_encoding
 		 */
 		function _processPhrases($phrases, $language_id, $language_encoding)
 		{
 			static $other_translations = Array ();
 
 			if ( $this->Application->isDebugMode() ) {
 				$this->Application->Debugger->profileStart('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import');
 			}
 
 			foreach ($phrases as $phrase_node) {
 				/* @var $phrase_node SimpleXMLElement */
 
 				$phrase_key = mb_strtoupper($phrase_node['Label']);
 
 				$fields_hash = Array (
 					'Phrase' => (string)$phrase_node['Label'],
 					'PhraseKey' => $phrase_key,
 					'PhraseType' => (int)$phrase_node['Type'],
 					'Module' => (string)$phrase_node['Module'] ? (string)$phrase_node['Module'] : 'Core',
 					'LastChanged' => TIMENOW,
 					'LastChangeIP' => $this->ip_address,
 				);
 
 				$translation = (string)$phrase_node;
 				$hint_translation = (string)$phrase_node['Hint'];
 				$column_translation = (string)$phrase_node['Column'];
 
 				if ( array_key_exists($fields_hash['PhraseType'], $this->phrase_types_allowed) ) {
 					if ( $language_encoding != 'plain' ) {
 						$translation = base64_decode($translation);
 						$hint_translation = base64_decode($hint_translation);
 						$column_translation = base64_decode($column_translation);
 					}
 
 					if ( array_key_exists($phrase_key, $other_translations) ) {
 						$other_translations[$phrase_key]['l' . $language_id . '_Translation'] = $translation;
 						$other_translations[$phrase_key]['l' . $language_id . '_HintTranslation'] = $hint_translation;
 						$other_translations[$phrase_key]['l' . $language_id . '_ColumnTranslation'] = $column_translation;
 					}
 					else {
 						$other_translations[$phrase_key] = Array (
 							'l' . $language_id . '_Translation' => $translation,
 							'l' . $language_id . '_HintTranslation' => $hint_translation,
 							'l' . $language_id . '_ColumnTranslation' => $column_translation,
 						);
 					}
 
 					$fields_hash = array_merge($fields_hash, $other_translations[$phrase_key]);
 					$this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE', false);
 				}
 			}
 
 			if ( $this->Application->isDebugMode() ) {
 				$this->Application->Debugger->profileFinish('L[' . $language_id . ']P', 'Language: ' . $language_id . '; Phrases Import');
 			}
 
 			$this->Conn->doInsert($fields_hash, $this->_tables['phrases'], 'REPLACE');
 		}
 
 		/**
 		 * Performs email event import
 		 *
 		 * @param SimpleXMLElement $events
 		 * @param int $language_id
 		 * @param string $language_encoding
 		 */
 		function _processEvents($events, $language_id, $language_encoding)
 		{
 			static $other_translations = Array ();
 
 			if ( $this->Application->isDebugMode() ) {
 				$this->Application->Debugger->profileStart('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import');
 			}
 
 			$email_message_helper = $this->Application->recallObject('kEmailMessageHelper');
 			/* @var $email_message_helper kEmailMessageHelper */
 
 			foreach ($events as $event_node) {
 				/* @var $event_node SimpleXMLElement */
 
 				$message_type = (string)$event_node['MessageType'];
 				$event_id = $this->_getEventId((string)$event_node['Event'], (int)$event_node['Type']);
 
 				if ( !$event_id ) {
 					continue;
 				}
 
 				$fields_hash = Array (
 					'EventId' => $event_id,
 					'Event' => (string)$event_node['Event'],
 					'Type' => (int)$event_node['Type'],
 				);
 
 				if ( $message_type == '' ) {
 					$parsed = $email_message_helper->parseTemplate($event_node, '');
 					$parsed = array_map($language_encoding == 'plain' ? 'rtrim' : 'base64_decode', $parsed);
 
 				}
 				else {
 					$template = $language_encoding == 'plain' ? rtrim($event_node) : base64_decode($event_node);
 					$parsed = $email_message_helper->parseTemplate($template, $message_type);
 				}
 
 				if ( array_key_exists($event_id, $other_translations) ) {
 					$other_translations[$event_id]['l' . $language_id . '_Subject'] = $parsed['Subject'];
 					$other_translations[$event_id]['l' . $language_id . '_HtmlBody'] = $parsed['HtmlBody'];
 					$other_translations[$event_id]['l' . $language_id . '_PlainTextBody'] = $parsed['PlainTextBody'];
 				}
 				else {
 					$other_translations[$event_id] = Array (
 						'l' . $language_id . '_Subject' => $parsed['Subject'],
 						'l' . $language_id . '_HtmlBody' => $parsed['HtmlBody'],
 						'l' . $language_id . '_PlainTextBody' => $parsed['PlainTextBody'],
 					);
 				}
 
 				if ( $parsed['Headers'] ) {
 					$other_translations[$event_id]['Headers'] = $parsed['Headers'];
 				}
 				elseif ( !$parsed['Headers'] && !array_key_exists('Headers', $other_translations[$event_id]) ) {
 					$other_translations[$event_id]['Headers'] = $parsed['Headers'];
 				}
 
 				$fields_hash = array_merge($fields_hash, $other_translations[$event_id]);
 				$this->Conn->doInsert($fields_hash, $this->_tables['emailevents'], 'REPLACE', false);
 			}
 
 			if ( $this->Application->isDebugMode() ) {
 				$this->Application->Debugger->profileFinish('L[' . $language_id . ']E', 'Language: ' . $language_id . '; Events Import');
 			}
 
 			if ( isset($fields_hash) ) {
 				// at least one email event in language pack was found in database
 				$this->Conn->doInsert($fields_hash, $this->_tables['emailevents'], 'REPLACE');
 			}
 		}
 
 		/**
 		 * Performs country_state translation import
 		 *
 		 * @param SimpleXMLElement $country_states
 		 * @param int $language_id
 		 * @param string $language_encoding
 		 * @param bool $process_states
 		 * @return void
 		 */
 		function _processCountries($country_states, $language_id, $language_encoding, $process_states = false)
 		{
 			static $other_translations = Array ();
 
 			foreach ($country_states as $country_state_node) {
 				/* @var $country_state_node SimpleXMLElement */
 
 				if ( $process_states ) {
 					$country_state_id = $this->_getStateId((string)$country_states['Iso'], (string)$country_state_node['Iso']);
 				}
 				else {
 					$country_state_id = $this->_getCountryId((string)$country_state_node['Iso']);
 				}
 
 				if ( !$country_state_id ) {
 					continue;
 				}
 
 				if ( $language_encoding == 'plain' ) {
 					$translation = rtrim($country_state_node['Translation']);
 				}
 				else {
 					$translation = base64_decode($country_state_node['Translation']);
 				}
 
 				$fields_hash = Array ('CountryStateId' => $country_state_id);
 
 				if ( array_key_exists($country_state_id, $other_translations) ) {
 					$other_translations[$country_state_id]['l' . $language_id . '_Name'] = $translation;
 				}
 				else {
 					$other_translations[$country_state_id] = Array ('l' . $language_id . '_Name' => $translation);
 				}
 
 				$fields_hash = array_merge($fields_hash, $other_translations[$country_state_id]);
 				$this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE', false);
 
 				// PHP 5.3 version would be: $country_state_node->count()
 				if ( !$process_states && count($country_state_node->children()) ) {
 					$this->_processCountries($country_state_node, $language_id, $language_encoding, true);
 				}
 			}
 
 			$this->Conn->doInsert($fields_hash, $this->_tables['country-state'], 'REPLACE');
 		}
 
 		/**
 		 * Creates/updates language based on given fields and returns it's id
 		 *
 		 * @param Array $fields_hash
 		 * @return int
 		 */
 		function _processLanguage($fields_hash)
 		{
 			// 1. get language from database
 			$sql = 'SELECT ' . $this->lang_object->IDField . '
 					FROM ' . $this->lang_object->TableName . '
 					WHERE PackName = ' . $this->Conn->qstr($fields_hash['PackName']);
 			$language_id = $this->Conn->GetOne($sql);
 
 			if ($language_id) {
 				// 2. language found -> update, when allowed
 				$this->lang_object->Load($language_id);
 
 				if ($this->import_mode == LANG_OVERWRITE_EXISTING) {
 					// update live language record based on data from xml
 					$this->lang_object->SetFieldsFromHash($fields_hash);
 					$this->lang_object->Update();
 				}
 			}
 			else {
 				// 3. language not found -> create
 				$this->lang_object->SetFieldsFromHash($fields_hash);
 				$this->lang_object->SetDBField('Enabled', STATUS_ACTIVE);
 
 				if ($this->lang_object->Create()) {
 					$language_id = $this->lang_object->GetID();
 
 					if (defined('IS_INSTALL') && IS_INSTALL) {
 						// language created during install becomes admin interface language
 						$this->lang_object->setPrimary(true, true);
 					}
 				}
 			}
 
 			// 4. collect ID of every processed language
 			if (!in_array($language_id, $this->_languages)) {
 				$this->_languages[] = $language_id;
 			}
 
 			return $language_id;
 		}
 
 		/**
 		 * Returns event id based on it's name and type
 		 *
 		 * @param string $event_name
 		 * @param string $event_type
 		 * @return int
 		 */
 		function _getEventId($event_name, $event_type)
 		{
 			$cache_key = $event_name . '_' . $event_type;
 
 			return array_key_exists($cache_key, $this->events_hash) ? $this->events_hash[$cache_key] : 0;
 		}
 
 		/**
 		 * Returns country id based on it's 3letter ISO code
 		 *
 		 * @param string $iso
 		 * @return int
 		 */
 		function _getCountryId($iso)
 		{
 			static $cache = null;
 
 			if (!isset($cache)) {
 				$sql = 'SELECT CountryStateId, IsoCode
 						FROM ' . TABLE_PREFIX . 'CountryStates
 						WHERE Type = ' . DESTINATION_TYPE_COUNTRY;
 				$cache = $this->Conn->GetCol($sql, 'IsoCode');
 			}
 
 			return array_key_exists($iso, $cache) ? $cache[$iso] : false;
 		}
 
 		/**
 		 * Returns state id based on 3letter country ISO code and 2letter state ISO code
 		 *
 		 * @param string $country_iso
 		 * @param string $state_iso
 		 * @return int
 		 */
 		function _getStateId($country_iso, $state_iso)
 		{
 			static $cache = null;
 
 			if (!isset($cache)) {
 				$sql = 'SELECT CountryStateId, CONCAT(StateCountryId, "-", IsoCode) AS IsoCode
 						FROM ' . TABLE_PREFIX . 'CountryStates
 						WHERE Type = ' . DESTINATION_TYPE_STATE;
 				$cache = $this->Conn->GetCol($sql, 'IsoCode');
 			}
 
 			$country_id = $this->_getCountryId($country_iso);
 
 			return array_key_exists($country_id . '-' . $state_iso, $cache) ? $cache[$country_id . '-' . $state_iso] : false;
 		}
 
 		/**
 		 * Returns comma-separated list of IDs, that will be exported
 		 *
 		 * @param string $prefix
 		 * @return string
 		 * @access public
 		 */
 		public function getExportIDs($prefix)
 		{
 			$ids = $this->Application->RecallVar($prefix . '_selected_ids');
 
 			if ( $ids ) {
 				// some records were selected in grid
 				return $ids;
 			}
 
 			$tag_params = Array (
 				'grid' => $prefix == 'phrases' ? 'Phrases' : 'Emails',
 				'skip_counting' => 1,
 				'per_page' => -1
 			);
 
 			$list = $this->Application->recallObject($prefix, $prefix . '_List', $tag_params);
 			/* @var $list kDBList */
 
 			$sql = $list->getCountSQL($list->GetSelectSQL());
 			$sql = str_replace('COUNT(*) AS count', $list->TableName . '.' . $list->IDField, $sql);
 
 			$ids = '';
-			$rs = $this->Conn->QueryRaw($sql);
+			$rows = $this->Conn->GetIterator($sql);
 
-			if ( $this->Conn->RowCount($rs) ) {
-				while ( ($row = $this->Conn->GetNextRow($rs)) ) {
+			if ( count($rows) ) {
+				foreach ($rows as $row) {
 					$ids .= ',' . $row[$list->IDField];
 				}
 
 				$ids = substr($ids, 1);
 			}
 
-			$this->Conn->Destroy($rs);
-
 			return $ids;
 		}
 	}
\ No newline at end of file