Index: branches/5.0.x/core/kernel/utility/temp_handler.php
===================================================================
--- branches/5.0.x/core/kernel/utility/temp_handler.php	(revision 13602)
+++ branches/5.0.x/core/kernel/utility/temp_handler.php	(revision 13603)
@@ -1,946 +1,944 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 defined('FULL_PATH') or die('restricted access!');
 
 class kTempTablesHandler extends kBase {
 
 	var $Tables = Array();
 
 	/**
 	 * Master table name for temp handler
 	 *
 	 * @var string
 	 * @access private
 	 */
 	var $MasterTable = '';
 
 	/**
 	 * IDs from master table
 	 *
 	 * @var Array
 	 * @access private
 	 */
 	var $MasterIDs = Array();
 
 	var $AlreadyProcessed = Array();
 
 	var $DroppedTables = Array();
 
 	var $FinalRefs = Array();
 
 	var $CopiedTables =  Array();
 
 
 	/**
 	 * IDs of newly cloned items (key - prefix.special, value - array of ids)
 	 *
 	 * @var Array
 	 */
 	var $savedIDs = Array();
 
 	/**
 	* Description
 	*
 	* @var kDBConnection
 	* @access public
 	*/
 	var $Conn;
 
 
 	/**
 	 * Window ID of current window
 	 *
 	 * @var mixed
 	 */
 	var $WindowID = '';
 
 	function kTempTablesHandler()
 	{
 		parent::kBase();
 		$this->Conn =& $this->Application->GetADODBConnection();
 	}
 
 	function SetTables($tables)
 	{
 		// set tablename as key for tables array
 		$ret = Array();
 		$this->Tables = $tables;
 		$this->MasterTable = $tables['TableName'];
 	}
 
 	function saveID($prefix, $special = '', $id = null)
 	{
 		if (!isset($this->savedIDs[$prefix.($special ? '.' : '').$special])) {
 			$this->savedIDs[$prefix.($special ? '.' : '').$special] = array();
 		}
 		if (is_array($id)) {
 			foreach ($id as $tmp_id => $live_id) {
 				$this->savedIDs[$prefix.($special ? '.' : '').$special][$tmp_id] = $live_id;
 			}
 		}
 		else {
 			$this->savedIDs[$prefix.($special ? '.' : '').$special][] = $id;
 		}
 	}
 
 	/**
 	 * Get temp table name
 	 *
 	 * @param string $table
 	 * @return string
 	 */
 	function GetTempName($table)
 	{
 		return $this->Application->GetTempName($table, $this->WindowID);
 	}
 
 	function GetTempTablePrefix()
 	{
 		return $this->Application->GetTempTablePrefix($this->WindowID);
 	}
 
 	/**
 	 * Return live table name based on temp table name
 	 *
 	 * @param string $temp_table
 	 * @return string
 	 */
 	function GetLiveName($temp_table)
 	{
 		return $this->Application->GetLiveName($temp_table);
 	}
 
 	function IsTempTable($table)
 	{
 		return $this->Application->IsTempTable($table);
 	}
 
 	/**
 	 * Return temporary table name for master table
 	 *
 	 * @return string
 	 * @access public
 	 */
 	function GetMasterTempName()
 	{
 		return $this->GetTempName($this->MasterTable);
 	}
 
 	function CreateTempTable($table)
 	{
 		$query = sprintf("CREATE TABLE %s SELECT * FROM %s WHERE 0",
 										$this->GetTempName($table),
 										$table);
 
 		$this->Conn->Query($query);
 	}
 
 	function BuildTables($prefix, $ids)
 	{
 		$this->WindowID = $this->Application->GetVar('m_wid');
 
 		$this->TableIdCounter = 0;
 		$tables = Array(
 				'TableName'	=>	$this->Application->getUnitOption($prefix, 'TableName'),
 				'IdField'	=>	$this->Application->getUnitOption($prefix, 'IDField'),
 				'IDs'		=>	$ids,
 				'Prefix'	=>	$prefix,
 				'TableId'	=>	$this->TableIdCounter++,
 		);
 
 		/*$parent_prefix = $this->Application->getUnitOption($prefix, 'ParentPrefix');
 		if ($parent_prefix) {
 			$tables['ForeignKey'] = $this->Application->getUnitOption($prefix, 'ForeignKey');
 			$tables['ParentPrefix'] = $parent_prefix;
 			$tables['ParentTableKey'] = $this->Application->getUnitOption($prefix, 'ParentTableKey');
 		}*/
 
 		$this->FinalRefs[ $tables['TableName'] ] = $tables['TableId'];	// don't forget to add main table to FinalRefs too
 
 		$SubItems = $this->Application->getUnitOption($prefix,'SubItems');
 		if (is_array($SubItems)) {
 			foreach ($SubItems as $prefix) {
 				$this->AddTables($prefix, $tables);
 			}
 		}
 		$this->SetTables($tables);
 	}
 
 	/**
 	 * Searches through TempHandler tables info for required prefix
 	 *
 	 * @param string $prefix
 	 * @param Array $master
 	 * @return mixed
 	 */
 	function SearchTable($prefix, $master = null)
 	{
 		if (is_null($master)) {
 			$master = $this->Tables;
 		}
 
 		if ($master['Prefix'] == $prefix) {
 			return $master;
 		}
 
 		if (isset($master['SubTables'])) {
 			foreach ($master['SubTables'] as $sub_table) {
 				$found = $this->SearchTable($prefix, $sub_table);
 				if ($found !== false) {
 					return $found;
 				}
 			}
 		}
 
 		return false;
 	}
 
 	function AddTables($prefix, &$tables)
 	{
 		if (!$this->Application->prefixRegistred($prefix)) {
 			// allows to skip subitem processing if subitem module not enabled/installed
 			return ;
 		}
 
 		$tmp = Array(
 				'TableName' => $this->Application->getUnitOption($prefix,'TableName'),
 				'IdField' => $this->Application->getUnitOption($prefix,'IDField'),
 				'ForeignKey' => $this->Application->getUnitOption($prefix,'ForeignKey'),
 				'ParentPrefix' => $this->Application->getUnitOption($prefix, 'ParentPrefix'),
 				'ParentTableKey' => $this->Application->getUnitOption($prefix,'ParentTableKey'),
 				'Prefix' => $prefix,
 				'AutoClone' => $this->Application->getUnitOption($prefix,'AutoClone'),
 				'AutoDelete' => $this->Application->getUnitOption($prefix,'AutoDelete'),
 				'TableId' => $this->TableIdCounter++,
 		);
 
 		$this->FinalRefs[ $tmp['TableName'] ] = $tmp['TableId'];
 
 		$constrain = $this->Application->getUnitOption($prefix,'Constrain');
 		if ($constrain)
 		{
 			$tmp['Constrain'] = $constrain;
 			$this->FinalRefs[ $tmp['TableName'].$tmp['Constrain'] ] = $tmp['TableId'];
 		}
 
 		$SubItems = $this->Application->getUnitOption($prefix,'SubItems');
 		$same_sub_counter = 1;
 		if( is_array($SubItems) )
 		{
 			foreach($SubItems as $prefix)
 			{
 				$this->AddTables($prefix, $tmp);
 			}
 		}
 
 		if ( !is_array(getArrayValue($tables, 'SubTables')) ) {
 			$tables['SubTables'] = array();
 		}
 
 		$tables['SubTables'][] = $tmp;
 	}
 
 	function CloneItems($prefix, $special, $ids, $master = null, $foreign_key = null, $parent_prefix = null, $skip_filenames = false)
 	{
 		if (!isset($master)) $master = $this->Tables;
 
 		// recalling by different name, because we may get kDBList, if we recall just by prefix
 		if (!preg_match('/(.*)-item$/', $special)) {
 			$special .= '-item';
 		}
 
 		$object =& $this->Application->recallObject($prefix.'.'.$special, $prefix, Array('skip_autoload' => true));
 		$object->PopulateMultiLangFields();
 
 		foreach ($ids as $id) {
 			$mode = 'create';
 			if ( $cloned_ids = getArrayValue($this->AlreadyProcessed, $master['TableName']) ) {
 				// if we have already cloned the id, replace it with cloned id and set mode to update
 				// update mode is needed to update second ForeignKey for items cloned by first ForeignKey
 				if ( getArrayValue($cloned_ids, $id) ) {
 					$id = $cloned_ids[$id];
 					$mode = 'update';
 				}
 			}
 
 			$object->Load($id);
 			$original_values = $object->FieldValues;
 
 			if (!$skip_filenames) {
 				$object->NameCopy($master, $foreign_key);
 			}
 			elseif ($master['TableName'] == $this->MasterTable) {
 				// kCatDBItem class only has this attribute
 				$object->useFilenames = false;
 			}
 
 			if (isset($foreign_key)) {
 				$master_foreign_key_field = is_array($master['ForeignKey']) ? $master['ForeignKey'][$parent_prefix] : $master['ForeignKey'];
 				$object->SetDBField($master_foreign_key_field, $foreign_key);
 			}
 
 			if ($mode == 'create') {
 				$this->RaiseEvent('OnBeforeClone', $master['Prefix'], $special, Array($object->GetId()), $foreign_key);
 			}
 
 			$object->inCloning = true;
 			$res = $mode == 'update' ? $object->Update() : $object->Create();
 			$object->inCloning = false;
 
 			if ($res)
 			{
 				if ( $mode == 'create' && is_array( getArrayValue($master, 'ForeignKey')) ) {
 					// remember original => clone mapping for dual ForeignKey updating
 					$this->AlreadyProcessed[$master['TableName']][$id] = $object->GetId();
 				}
-				if ($object->mode == 't') {
-					$object->setTempID();
-				}
+
 				if ($mode == 'create') {
 					$this->RaiseEvent('OnAfterClone', $master['Prefix'], $special, Array($object->GetId()), $foreign_key, array('original_id' => $id) );
 					$this->saveID($master['Prefix'], $special, $object->GetID());
 				}
 
 				if ( is_array(getArrayValue($master, 'SubTables')) ) {
 					foreach($master['SubTables'] as $sub_table) {
 						if (!getArrayValue($sub_table, 'AutoClone')) continue;
 						$sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName'];
 
 						$foreign_key_field = is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey'];
 						$parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey'];
 
 						if (!$foreign_key_field || !$parent_key_field) continue;
 
 						$query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.'
 											WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field];
 						if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain'];
 
 						$sub_ids = $this->Conn->GetCol($query);
 
 						if ( is_array(getArrayValue($sub_table, 'ForeignKey')) ) {
 							// $sub_ids could containt newly cloned items, we need to remove it here
 							// to escape double cloning
 
 							$cloned_ids = getArrayValue($this->AlreadyProcessed, $sub_table['TableName']);
 							if ( !$cloned_ids ) $cloned_ids = Array();
 							$new_ids = array_values($cloned_ids);
 							$sub_ids = array_diff($sub_ids, $new_ids);
 						}
 
 						$parent_key = $object->GetDBField($parent_key_field);
 
 						$this->CloneItems($sub_table['Prefix'], $special, $sub_ids, $sub_table, $parent_key, $master['Prefix']);
 					}
 				}
 			}
 		}
 
 		if (!$ids) {
 			$this->savedIDs[$prefix.($special ? '.' : '').$special] = Array();
 		}
 
 		return $this->savedIDs[$prefix.($special ? '.' : '').$special];
 	}
 
 	function DeleteItems($prefix, $special, $ids, $master=null, $foreign_key=null)
 	{
 		if (!isset($master)) $master = $this->Tables;
 		if( strpos($prefix,'.') !== false ) list($prefix,$special) = explode('.', $prefix, 2);
 
 		$prefix_special = rtrim($prefix.'.'.$special, '.');
 
 		//recalling by different name, because we may get kDBList, if we recall just by prefix
 		$recall_prefix = $prefix_special.($special ? '' : '.').'-item';
 		$object =& $this->Application->recallObject($recall_prefix, $prefix, Array('skip_autoload' => true));
 
 		foreach ($ids as $id)
 		{
 			$object->Load($id);
 			$original_values = $object->FieldValues;
 			if( !$object->Delete($id) ) continue;
 
 			if ( is_array(getArrayValue($master, 'SubTables')) ) {
 				foreach($master['SubTables'] as $sub_table) {
 					if (!getArrayValue($sub_table, 'AutoDelete')) continue;
 					$sub_TableName = ($object->mode == 't') ? $this->GetTempName($sub_table['TableName']) : $sub_table['TableName'];
 
 					$foreign_key_field = is_array($sub_table['ForeignKey']) ? getArrayValue($sub_table, 'ForeignKey', $master['Prefix']) : $sub_table['ForeignKey'];
 					$parent_key_field = is_array($sub_table['ParentTableKey']) ? getArrayValue($sub_table, 'ParentTableKey', $master['Prefix']) : $sub_table['ParentTableKey'];
 
 					if (!$foreign_key_field || !$parent_key_field) continue;
 
 					$query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.'
 										WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field];
 
 					$sub_ids = $this->Conn->GetCol($query);
 
 					$parent_key = $object->GetDBField(is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$prefix] : $sub_table['ParentTableKey']);
 
 					$this->DeleteItems($sub_table['Prefix'], '', $sub_ids, $sub_table, $parent_key);
 				}
 			}
 
 		}
 	}
 
 	function DoCopyLiveToTemp($master, $ids, $parent_prefix=null)
 	{
 		// when two tables refers the same table as sub-sub-table, and ForeignKey and ParentTableKey are arrays
 		// the table will be first copied by first sub-table, then dropped and copied over by last ForeignKey in the array
 		// this should not do any problems :)
 		if ( !preg_match("/.*\.[0-9]+/", $master['Prefix']) ) {
 			if( $this->DropTempTable($master['TableName']) )
 			{
 				$this->CreateTempTable($master['TableName']);
 			}
 		}
 
 		if (is_array($ids)) {
 			$ids = join(',', $ids);
 		}
 
 		$table_sig = $master['TableName'].(isset($master['Constrain']) ? $master['Constrain'] : '');
 
 		if ($ids != '' && !in_array($table_sig, $this->CopiedTables)) {
 			if ( getArrayValue($master, 'ForeignKey') ) {
 				if ( is_array($master['ForeignKey']) ) {
 					$key_field = $master['ForeignKey'][$parent_prefix];
 				}
 				else {
 					$key_field = $master['ForeignKey'];
 				}
 			}
 			else {
 				$key_field = $master['IdField'];
 			}
 
 			$query = 'INSERT INTO '.$this->GetTempName($master['TableName']).'
 									SELECT * FROM '.$master['TableName'].'
 									WHERE '.$key_field.' IN ('.$ids.')';
 			if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 			$this->Conn->Query($query);
 
 			$this->CopiedTables[] = $table_sig;
 
 			$query = 'SELECT '.$master['IdField'].' FROM '.$master['TableName'].'
 								WHERE '.$key_field.' IN ('.$ids.')';
 			if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 			$this->RaiseEvent( 'OnAfterCopyToTemp', $master['Prefix'], '', $this->Conn->GetCol($query) );
 		}
 
 		if ( getArrayValue($master, 'SubTables') ) {
 			foreach ($master['SubTables'] as $sub_table) {
 
 				$parent_key = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey'];
 				if (!$parent_key) continue;
 
 				if ( $ids != '' && $parent_key != $key_field ) {
 					$query = 'SELECT '.$parent_key.' FROM '.$master['TableName'].'
 										WHERE '.$key_field.' IN ('.$ids.')';
 					$sub_foreign_keys = join(',', $this->Conn->GetCol($query));
 				}
 				else {
 					$sub_foreign_keys = $ids;
 				}
 				$this->DoCopyLiveToTemp($sub_table, $sub_foreign_keys, $master['Prefix']);
 			}
 		}
 	}
 
 	function GetForeignKeys($master, $sub_table, $live_id, $temp_id=null)
 	{
 		$mode = 1; //multi
 		if (!is_array($live_id)) {
 			$live_id = Array($live_id);
 			$mode = 2; //single
 		}
 		if (isset($temp_id) && !is_array($temp_id)) $temp_id = Array($temp_id);
 
 		if ( isset($sub_table['ParentTableKey']) ) {
 			if ( is_array($sub_table['ParentTableKey']) ) {
 				$parent_key_field = $sub_table['ParentTableKey'][$master['Prefix']];
 			}
 			else {
 				$parent_key_field = $sub_table['ParentTableKey'];
 			}
 		}
 		else {
 			$parent_key_field = $master['IdField'];
 		}
 
 		if ( $cached = getArrayValue($this->FKeysCache, $master['TableName'].'.'.$parent_key_field) ) {
 			if ( array_key_exists(serialize($live_id), $cached) ) {
 				list($live_foreign_key, $temp_foreign_key) = $cached[serialize($live_id)];
 				if ($mode == 1) {
 					return $live_foreign_key;
 				}
 				else {
 					return Array($live_foreign_key[0], $temp_foreign_key[0]);
 				}
 			}
 		}
 
 		if ($parent_key_field != $master['IdField']) {
 			$query = 'SELECT '.$parent_key_field.' FROM '.$master['TableName'].'
 								WHERE '.$master['IdField'].' IN ('.join(',', $live_id).')';
 			$live_foreign_key = $this->Conn->GetCol($query);
 
 			if (isset($temp_id)) {
 				// because DoCopyTempToOriginal resets negative IDs to 0 in temp table (one by one) before copying to live
 				$temp_key = $temp_id < 0 ? 0 : $temp_id;
 				$query = 'SELECT '.$parent_key_field.' FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$master['IdField'].' IN ('.join(',', $temp_key).')';
 				$temp_foreign_key = $this->Conn->GetCol($query);
 			}
 			else {
 				$temp_foreign_key = Array();
 			}
 		}
 		else {
 			$live_foreign_key = $live_id;
 			$temp_foreign_key = $temp_id;
 		}
 
 		$this->FKeysCache[$master['TableName'].'.'.$parent_key_field][serialize($live_id)] = Array($live_foreign_key, $temp_foreign_key);
 
 		if ($mode == 1) {
 			return $live_foreign_key;
 		}
 		else {
 			return Array($live_foreign_key[0], $temp_foreign_key[0]);
 		}
 	}
 
 	function DoCopyTempToOriginal($master, $parent_prefix = null, $current_ids = Array())
 	{
 		if (!$current_ids) {
 			$query = 'SELECT '.$master['IdField'].' FROM '.$this->GetTempName($master['TableName']);
 			if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain'];
 			$current_ids = $this->Conn->GetCol($query);
 		}
 
 		$table_sig = $master['TableName'].(isset($master['Constrain']) ? $master['Constrain'] : '');
 
 		if ($current_ids) {
 			// delete all ids from live table - for MasterTable ONLY!
 			// because items from Sub Tables get deteleted in CopySubTablesToLive !BY ForeignKey!
 			if ($master['TableName'] == $this->MasterTable) {
 				$this->RaiseEvent( 'OnBeforeDeleteFromLive', $master['Prefix'], '', $current_ids );
 
 				$query = 'DELETE FROM '.$master['TableName'].' WHERE '.$master['IdField'].' IN ('.join(',', $current_ids).')';
 				$this->Conn->Query($query);
 			}
 
 			if ( getArrayValue($master, 'SubTables') ) {
 				if( in_array($table_sig, $this->CopiedTables) || $this->FinalRefs[$table_sig] != $master['TableId'] ) return;
 
 				foreach($current_ids AS $id)
 				{
 					$this->RaiseEvent( 'OnBeforeCopyToLive', $master['Prefix'], '', Array($id) );
 
 					//reset negative ids to 0, so autoincrement in live table works fine
 					if($id < 0)
 					{
 						$query = 'UPDATE '.$this->GetTempName($master['TableName']).'
 											SET '.$master['IdField'].' = 0
 											WHERE '.$master['IdField'].' = '.$id;
 						if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 						$this->Conn->Query($query);
 						$id_to_copy = 0;
 					}
 					else
 					{
 						$id_to_copy = $id;
 					}
 
 					//copy current id_to_copy (0 for new or real id) to live table
 					$query = 'INSERT INTO '.$master['TableName'].'
 										SELECT * FROM '.$this->GetTempName($master['TableName']).'
 										WHERE '.$master['IdField'].' = '.$id_to_copy;
 					$this->Conn->Query($query);
 					$insert_id = $id_to_copy == 0 ? $this->Conn->getInsertID() : $id_to_copy;
 
 					$this->saveID($master['Prefix'], '', array($id => $insert_id));
 					$this->RaiseEvent( 'OnAfterCopyToLive', $master['Prefix'], '', Array($insert_id), null, array('temp_id' => $id) );
 
 					$this->UpdateForeignKeys($master, $insert_id, $id);
 
 					//delete already copied record from master temp table
 					$query = 'DELETE FROM '.$this->GetTempName($master['TableName']).'
 										WHERE '.$master['IdField'].' = '.$id_to_copy;
 					if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 					$this->Conn->Query($query);
 				}
 
 				$this->CopiedTables[] = $table_sig;
 
 				// when all of ids in current master has been processed, copy all sub-tables data
 				$this->CopySubTablesToLive($master, $current_ids);
 			}
 			elseif( !in_array($table_sig, $this->CopiedTables) && ($this->FinalRefs[$table_sig] == $master['TableId']) ) { //If current master doesn't have sub-tables - we could use mass operations
 				// We don't need to delete items from live here, as it get deleted in the beggining of the method for MasterTable
 				// or in parent table processing for sub-tables
 				$this->RaiseEvent('OnBeforeCopyToLive', $master['Prefix'], '', $current_ids);
 
 				$live_ids = array();
 				foreach ($current_ids as $an_id) {
 					if ($an_id > 0) {
 						$live_ids[$an_id] = $an_id;
 						// positive (already live) IDs will be copied in on query all togather below,
 						// so we just store it here
 						continue;
 					}
 					else { // zero or negaitve ids should be copied one by one to get their InsertId
 						// reseting to 0 so it get inserted into live table with autoincrement
 						$query = 'UPDATE '.$this->GetTempName($master['TableName']).'
 									SET '.$master['IdField'].' = 0
 									WHERE '.$master['IdField'].' = '.$an_id;
 						// constrain is not needed here because ID is already unique
 						$this->Conn->Query($query);
 
 						// copying
 						$query = 'INSERT INTO '.$master['TableName'].'
 									SELECT * FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$master['IdField'].' = 0';
 						$this->Conn->Query($query);
 						$live_ids[$an_id] = $this->Conn->getInsertID(); //storing newly created live id
 
 						//delete already copied record from master temp table
 						$query = 'DELETE FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$master['IdField'].' = 0';
 						$this->Conn->Query($query);
 						$this->UpdateChangeLogForeignKeys($master, $live_ids[$an_id], $an_id);
 					}
 				}
 
 				// copy ALL records to live table
 				$query = 'INSERT INTO '.$master['TableName'].'
 									SELECT * FROM '.$this->GetTempName($master['TableName']);
 				if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain'];
 				$this->Conn->Query($query);
 
 				$this->CopiedTables[] = $table_sig;
 				$this->RaiseEvent('OnAfterCopyToLive', $master['Prefix'], '', $live_ids);
 
 				$this->saveID($master['Prefix'], '', $live_ids);
 
 				// no need to clear temp table - it will be dropped by next statement
 			}
 		}
 
 		if ($this->FinalRefs[ $master['TableName'] ] != $master['TableId']) return;
 
 		/*if ( is_array(getArrayValue($master, 'ForeignKey')) )	{ //if multiple ForeignKeys
 			if ( $master['ForeignKey'][$parent_prefix] != end($master['ForeignKey']) ) {
 				return; // Do not delete temp table if not all ForeignKeys have been processed (current is not the last)
 			}
 		}*/
 		$this->DropTempTable($master['TableName']);
 		$this->Application->resetCounters($master['TableName']);
 
 		if (!isset($this->savedIDs[ $master['Prefix'] ])) {
 			$this->savedIDs[ $master['Prefix'] ] = Array();
 		}
 
 		return $this->savedIDs[ $master['Prefix'] ];
 	}
 
 	/**
 	 * Create separate connection for locking purposes
 	 *
 	 * @return kDBConnection
 	 */
 	function &_getSeparateConnection()
 	{
 		static $connection = null;
 
 		if (!isset($connection)) {
 			$connection = new kDBConnection(SQL_TYPE, Array(&$this->Application, 'handleSQLError') );
 			$connection->debugMode = $this->Application->isDebugMode();
 			$connection->Connect(SQL_SERVER, SQL_USER, SQL_PASS, SQL_DB, true);
 		}
 
 		return $connection;
 	}
 
 	function UpdateChangeLogForeignKeys($master, $live_id, $temp_id)
 	{
 		$main_prefix = $this->Application->GetTopmostPrefix($master['Prefix']);
 		$ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
 		$changes = $this->Application->RecallVar($ses_var_name);
 		$changes = $changes ? unserialize($changes) : array();
 
 		foreach ($changes as $key => $rec) {
 			if ($rec['Prefix'] == $master['Prefix']) {
 				if ($rec['ItemId'] == $temp_id) {
 					$changes[$key]['ItemId'] = $live_id;
 				}
 			}
 			if ($rec['MasterPrefix'] == $master['Prefix']) {
 				if ($rec['MasterId'] == $temp_id) {
 					$changes[$key]['MasterId'] = $live_id;
 				}
 			}
 		}
 		$this->Application->StoreVar($ses_var_name, serialize($changes));
 	}
 
 	function UpdateForeignKeys($master, $live_id, $temp_id) {
 		$this->UpdateChangeLogForeignKeys($master, $live_id, $temp_id);
 		foreach ($master['SubTables'] as $sub_table) {
 			$foreign_key_field =  is_array($sub_table['ForeignKey']) ? getArrayValue($sub_table, 'ForeignKey', $master['Prefix']) : $sub_table['ForeignKey'];
 			if (!$foreign_key_field) {
 				continue;
 			}
 
 			list ($live_foreign_key, $temp_foreign_key) = $this->GetForeignKeys($master, $sub_table, $live_id, $temp_id);
 
 			//Update ForeignKey in sub TEMP table
 			if ($live_foreign_key != $temp_foreign_key) {
 				$query = 'UPDATE '.$this->GetTempName($sub_table['TableName']).'
 									SET '.$foreign_key_field.' = '.$live_foreign_key.'
 									WHERE '.$foreign_key_field.' = '.$temp_foreign_key;
 				if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain'];
 				$this->Conn->Query($query);
 			}
 		}
 	}
 
 	function CopySubTablesToLive($master, $current_ids) {
 		foreach ($master['SubTables'] as $sub_table) {
 
 			$table_sig = $sub_table['TableName'].(isset($sub_table['Constrain']) ? $sub_table['Constrain'] : '');
 
 			// delete records from live table by foreign key, so that records deleted from temp table
 			// get deleted from live
 			if (count($current_ids) > 0  && !in_array($table_sig, $this->CopiedTables) ) {
 				$foreign_key_field = is_array($sub_table['ForeignKey']) ? getArrayValue($sub_table, 'ForeignKey', $master['Prefix']) : $sub_table['ForeignKey'];
 				if (!$foreign_key_field) continue;
 				$foreign_keys = $this->GetForeignKeys($master, $sub_table, $current_ids);
 				if (count($foreign_keys) > 0) {
 					$query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_table['TableName'].'
 										WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')';
 					if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain'];
 
 					if ( $this->RaiseEvent( 'OnBeforeDeleteFromLive', $sub_table['Prefix'], '', $this->Conn->GetCol($query), $foreign_keys ) ){
 						$query = 'DELETE FROM '.$sub_table['TableName'].'
 											WHERE '.$foreign_key_field.' IN ('.join(',', $foreign_keys).')';
 						if (isset($sub_table['Constrain'])) $query .= ' AND '.$sub_table['Constrain'];
 						$this->Conn->Query($query);
 					}
 				}
 			}
 			//sub_table passed here becomes master in the method, and recursively updated and copy its sub tables
 			$this->DoCopyTempToOriginal($sub_table, $master['Prefix']);
 		}
 	}
 
 	function RaiseEvent($name, $prefix, $special, $ids, $foreign_key = null, $add_params = null)
 	{
 		if ( !is_array($ids) ) return ;
 
 		$event_key = $prefix.($special ? '.' : '').$special.':'.$name;
 		$event = new kEvent($event_key);
 		if (isset($foreign_key)) {
 			$event->setEventParam('foreign_key', $foreign_key);
 		}
 
 		$set_temp_id = ($name == 'OnAfterCopyToLive') && (!is_array($add_params) || !array_key_exists('temp_id', $add_params));
 
 		foreach($ids as $index => $id) {
 			$event->setEventParam('id', $id);
 
 			if ($set_temp_id) {
 				$event->setEventParam('temp_id', $index);
 			}
 
 			if (is_array($add_params)) {
 				foreach ($add_params as $name => $val) {
 					$event->setEventParam($name, $val);
 				}
 			}
 
 			$this->Application->HandleEvent($event);
 		}
 
 		return $event->status == erSUCCESS;
 	}
 
 	function DropTempTable($table)
 	{
 		if( in_array($table, $this->DroppedTables) ) return false;
 		$query = sprintf("DROP TABLE IF EXISTS %s",
 											$this->GetTempName($table)
 										);
 		array_push($this->DroppedTables, $table);
 		$this->DroppedTables = array_unique($this->DroppedTables);
 		$this->Conn->Query($query);
 
 		return true;
 	}
 
 	function PrepareEdit()
 	{
 		$this->DoCopyLiveToTemp($this->Tables, $this->Tables['IDs']);
 		if ($this->Application->getUnitOption($this->Tables['Prefix'],'CheckSimulatniousEdit')) {
 			$this->CheckSimultaniousEdit();
 		}
 	}
 
 	function SaveEdit($master_ids = Array())
 	{
 		// SessionKey field is required for deleting records from expired sessions
 		$conn =& $this->_getSeparateConnection();
 
 		$sleep_count = 0;
 		do {
 			// aquire lock
 			$conn->ChangeQuery('LOCK TABLES '.TABLE_PREFIX.'Semaphores WRITE');
 
 			$sql = 'SELECT SessionKey
 					FROM ' . TABLE_PREFIX . 'Semaphores
 					WHERE (MainPrefix = ' . $conn->qstr($this->Tables['Prefix']) . ')';
 			$another_coping_active = $conn->GetOne($sql);
 
 			if ($another_coping_active) {
 				// another user is coping data from temp table to live -> release lock and try again after 1 second
 				$conn->ChangeQuery('UNLOCK TABLES');
 				$sleep_count++;
 				sleep(1);
 			}
 		} while ($another_coping_active && ($sleep_count <= 30));
 
 		if ($sleep_count > 30) {
 			// another coping process failed to finished in 30 seconds
 			$error_message = $this->Application->Phrase('la_error_TemporaryTableCopingFailed');
 			$this->Application->SetVar('_temp_table_message', $error_message);
 
 			return false;
 		}
 
 		// mark, that we are coping from temp to live right now, so other similar attempt (from another script) will fail
 		$fields_hash = Array (
 			'SessionKey' => $this->Application->GetSID(),
 			'Timestamp' => adodb_mktime(),
 			'MainPrefix' => $this->Tables['Prefix'],
 		);
 
 		$conn->doInsert($fields_hash, TABLE_PREFIX.'Semaphores');
 		$semaphore_id = $conn->getInsertID();
 
 		// unlock table now to prevent permanent lock in case, when coping will end with SQL error in the middle
 		$conn->ChangeQuery('UNLOCK TABLES');
 
 		$ids = $this->DoCopyTempToOriginal($this->Tables, null, $master_ids);
 
 		// remove mark, that we are coping from temp to live
 		$conn->Query('LOCK TABLES '.TABLE_PREFIX.'Semaphores WRITE');
 
 		$sql = 'DELETE FROM ' . TABLE_PREFIX . 'Semaphores
 				WHERE SemaphoreId = ' . $semaphore_id;
 		$conn->ChangeQuery($sql);
 
 		$conn->ChangeQuery('UNLOCK TABLES');
 
 		return $ids;
 	}
 
 	function CancelEdit($master=null)
 	{
 		if (!isset($master)) $master = $this->Tables;
 		$this->DropTempTable($master['TableName']);
 		if ( getArrayValue($master, 'SubTables') ) {
 			foreach ($master['SubTables'] as $sub_table) {
 				$this->CancelEdit($sub_table);
 			}
 		}
 	}
 
 	/**
 	 * Checks, that someone is editing selected records and returns true, when no one.
 	 *
 	 * @param Array $ids
 	 *
 	 * @return bool
 	 */
 	function CheckSimultaniousEdit($ids = null)
 	{
 		$tables = $this->Conn->GetCol('SHOW TABLES');
 		$mask_edit_table = '/' . TABLE_PREFIX . 'ses_(.*)_edit_' . $this->MasterTable . '$/';
 
 		$my_sid = $this->Application->GetSID();
 		$my_wid = $this->Application->GetVar('m_wid');
 		$ids = implode(',', isset($ids) ? $ids : $this->Tables['IDs']);
 		$sids = Array ();
 		if (!$ids) {
 			return true;
 		}
 
 		foreach ($tables as $table) {
 			if ( preg_match($mask_edit_table, $table, $rets) ) {
 				$sid = preg_replace('/(.*)_(.*)/', '\\1', $rets[1]); // remove popup's wid from sid
 				if ($sid == $my_sid) {
 					if ($my_wid) {
 						// using popups for editing
 						if (preg_replace('/(.*)_(.*)/', '\\2', $rets[1]) == $my_wid) {
 							// don't count window, that is being opened right now
 							continue;
 						}
 					}
 					else {
 						// not using popups for editing -> don't count my session tables
 						continue;
 					}
 				}
 
 				$sql = 'SELECT COUNT(' . $this->Tables['IdField'] . ')
 						FROM ' . $table . '
 						WHERE ' . $this->Tables['IdField'] . ' IN (' . $ids . ')';
 				$found = $this->Conn->GetOne($sql);
 
 				if (!$found || in_array($sid, $sids)) {
 					continue;
 				}
 
 				$sids[] = $sid;
 			}
 		}
 
 		if ($sids) {
 			// detect who is it
 			$sql = 'SELECT
 						CONCAT(IF (s.PortalUserId = -1, \'root\',
 							IF (s.PortalUserId = -2, \'Guest\',
 								CONCAT(FirstName, \' \', LastName, \' (\', Login, \')\')
 							)
 						), \' IP: \', s.IpAddress, \'\') FROM ' . TABLE_PREFIX . 'UserSession AS s
 					LEFT JOIN ' . TABLE_PREFIX . 'PortalUser AS u
 					ON u.PortalUserId = s.PortalUserId
 					WHERE s.SessionKey IN (' . implode(',', $sids) . ')';
 			$users = $this->Conn->GetCol($sql);
 
 			if ($users) {
 				$this->Application->SetVar('_simultanious_edit_message',
 					sprintf($this->Application->Phrase('la_record_being_edited_by'), join(",\n", $users))
 				);
 
 				return false;
 			}
 		}
 
 		return true;
 	}
 
 }
\ No newline at end of file
Index: branches/5.0.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.0.x/core/kernel/db/db_event_handler.php	(revision 13602)
+++ branches/5.0.x/core/kernel/db/db_event_handler.php	(revision 13603)
@@ -1,2603 +1,2597 @@
 <?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!');
 
 	define('EH_CUSTOM_PROCESSING_BEFORE',1);
 	define('EH_CUSTOM_PROCESSING_AFTER',2);
 
 	/**
 	 * Note:
 	 *   1. When adressing variables from submit containing
 	 *	 	Prefix_Special as part of their name use
 	 *	 	$event->getPrefixSpecial(true) instead of
 	 *	 	$event->Prefix_Special as usual. This is due PHP
 	 *	 	is converting "." symbols in variable names during
 	 *	 	submit info "_". $event->getPrefixSpecial optional
 	 *	 	1st parameter returns correct corrent Prefix_Special
 	 *	 	for variables beeing submitted such way (e.g. variable
 	 *	 	name that will be converted by PHP: "users.read_only_id"
 	 *	 	will be submitted as "users_read_only_id".
 	 *
 	 *	 2.	When using $this->Application-LinkVar on variables submitted
 	 *		from form which contain $Prefix_Special then note 1st item. Example:
 	 *		LinkVar($event->getPrefixSpecial(true).'_varname',$event->Prefix_Special.'_varname')
 	 *
 	 */
 
 
 	/**
 	 * EventHandler that is used to process
 	 * any database related events
 	 *
 	 */
 	class kDBEventHandler extends kEventHandler {
 
 		/**
 		* Description
 		*
 		* @var kDBConnection
 		* @access public
 		*/
 		var $Conn;
 
 		/**
 		 * Adds ability to address db connection
 		 *
 		 * @return kDBEventHandler
 		 * @access public
 		 */
 		function kDBEventHandler()
 		{
 			parent::kBase();
 			$this->Conn =& $this->Application->GetADODBConnection();
 		}
 
 		/**
 		 * Checks permissions of user
 		 *
 		 * @param kEvent $event
 		 */
 		function CheckPermission(&$event)
 		{
 			if (!$this->Application->isAdmin) {
 				$allow_events = Array('OnSearch', 'OnSearchReset', 'OnNew');
 				if (in_array($event->Name, $allow_events)) {
 					// allow search on front
 					return true;
 				}
 			}
 
 			$section = $event->getSection();
 			if (!preg_match('/^CATEGORY:(.*)/', $section)) {
 				// only if not category item events
 				if ((substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave')) {
 					if ($this->isNewItemCreate($event)) {
 						return $this->Application->CheckPermission($section.'.add', 1);
 					}
 					else {
 						return $this->Application->CheckPermission($section.'.add', 1) || $this->Application->CheckPermission($section.'.edit', 1);
 					}
 				}
 			}
 
 			if ($event->Name == 'OnPreCreate') {
 				// save category_id before item create (for item category selector not to destroy permission checking category)
 				$this->Application->LinkVar('m_cat_id');
 			}
 
 			if ($event->Name == 'OnSaveWidths') {
 				return $this->Application->isAdminUser;
 			}
 
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Allows to override standart permission mapping
 		 *
 		 */
 		function mapPermissions()
 		{
 			parent::mapPermissions();
 			$permissions = Array(
 									'OnLoad'				=>	Array('self' => 'view', 'subitem' => 'view'),
 									'OnItemBuild'			=>	Array('self' => 'view', 'subitem' => 'view'),
 									'OnSuggestValues'		=>	Array('self' => 'view', 'subitem' => 'view'),
 
 									'OnBuild'				=>	Array('self' => true),
 
 									'OnNew'					=>	Array('self' => 'add', 'subitem' => 'add|edit'),
 									'OnCreate'				=>	Array('self' => 'add', 'subitem' => 'add|edit'),
 									'OnUpdate'				=>	Array('self' => 'edit', 'subitem' => 'add|edit'),
 									'OnSetPrimary'			=>	Array('self' => 'add|edit', 'subitem' => 'add|edit'),
 									'OnDelete'				=>	Array('self' => 'delete', 'subitem' => 'add|edit'),
 									'OnDeleteAll'			=>	Array('self' => 'delete', 'subitem' => 'add|edit'),
 									'OnMassDelete'			=>	Array('self' => 'delete', 'subitem' => 'add|edit'),
 									'OnMassClone'			=>	Array('self' => 'add', 'subitem' => 'add|edit'),
 
 									'OnCut'	=> array('self'=>'edit', 'subitem' => 'edit'),
 									'OnCopy'	=> array('self'=>'edit', 'subitem' => 'edit'),
 									'OnPaste'	=> array('self'=>'edit', 'subitem' => 'edit'),
 
 									'OnSelectItems'			=>	Array('self' => 'add|edit', 'subitem' => 'add|edit'),
 									'OnProcessSelected'		=>	Array('self' => 'add|edit', 'subitem' => 'add|edit'),
 									'OnSelectUser'			=>	Array('self' => 'add|edit', 'subitem' => 'add|edit'),
 
 									'OnMassApprove'			=>	Array('self' => 'advanced:approve|edit', 'subitem' => 'advanced:approve|add|edit'),
 									'OnMassDecline'			=>	Array('self' => 'advanced:decline|edit', 'subitem' => 'advanced:decline|add|edit'),
 									'OnMassMoveUp'			=>	Array('self' => 'advanced:move_up|edit', 'subitem' => 'advanced:move_up|add|edit'),
 									'OnMassMoveDown'		=>	Array('self' => 'advanced:move_down|edit', 'subitem' => 'advanced:move_down|add|edit'),
 
 									'OnPreCreate'			=>	Array('self' => 'add|add.pending', 'subitem' => 'edit|edit.pending'),
 									'OnEdit'				=>	Array('self' => 'edit|edit.pending', 'subitem' => 'edit|edit.pending'),
 
 									'OnExport'					=>	Array('self' => 'view|advanced:export'),
 									'OnExportBegin'				=>	Array('self' => 'view|advanced:export'),
 									'OnExportProgress'			=>	Array('self' => 'view|advanced:export'),
 
 
 									'OnSetAutoRefreshInterval' => Array ('self' => true, 'subitem' => true),
 									'OnAutoRefreshToggle' => Array ('self' => true, 'subitem' => true),
 
 									// theese event do not harm, but just in case check them too :)
 									'OnCancelEdit'			=>	Array('self' => true, 'subitem' => true),
 									'OnCancel'				=>	Array('self' => true, 'subitem' => true),
 									'OnReset'				=>	Array('self' => true, 'subitem' => true),
 
 									'OnSetSorting'			=>	Array('self' => true, 'subitem' => true),
 									'OnSetSortingDirect'	=>	Array('self' => true, 'subitem' => true),
 									'OnResetSorting'	=>	Array('self' => true, 'subitem' => true),
 
 									'OnSetFilter'			=>	Array('self' => true, 'subitem' => true),
 									'OnApplyFilters'		=>	Array('self' => true, 'subitem' => true),
 									'OnRemoveFilters'		=>	Array('self' => true, 'subitem' => true),
 									'OnSetFilterPattern'		=>	Array('self' => true, 'subitem' => true),
 
 									'OnSetPerPage'			=>	Array('self' => true, 'subitem' => true),
 									'OnSetPage'				=>	Array('self' => true, 'subitem' => true),
 
 									'OnSearch'				=>	Array('self' => true, 'subitem' => true),
 									'OnSearchReset'			=>	Array('self' => true, 'subitem' => true),
 
 									'OnGoBack'				=>	Array('self' => true, 'subitem' => true),
 
 									// it checks permission itself since flash uploader does not send cookies
 									'OnUploadFile' => Array ('self' => true, 'subitem' => true),
 									'OnDeleteFile' => Array ('self' => true, 'subitem' => true),
 
 									'OnViewFile' => Array ('self' => true, 'subitem' => true),
 									'OnSaveWidths' => Array ('self' => true, 'subitem' => true),
 
 									'OnValidateMInputFields' => Array ('self' => 'view'),
 							);
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		function mapEvents()
 		{
 			$events_map = Array(
 								'OnRemoveFilters'	=>	'FilterAction',
 								'OnApplyFilters'	=>	'FilterAction',
 								'OnMassApprove'=>'iterateItems',
 								'OnMassDecline'=>'iterateItems',
 								'OnMassMoveUp'=>'iterateItems',
 								'OnMassMoveDown'=>'iterateItems',
 								);
 
 			$this->eventMethods = array_merge($this->eventMethods, $events_map);
 		}
 
 		/**
 		 * Returns ID of current item to be edited
 		 * by checking ID passed in get/post as prefix_id
 		 * or by looking at first from selected ids, stored.
 		 * Returned id is also stored in Session in case
 		 * it was explicitly passed as get/post
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function getPassedID(&$event)
 		{
 			if ($event->getEventParam('raise_warnings') === false) {
 				$event->setEventParam('raise_warnings', 1);
 			}
 
 			if (preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1])) {
 				// <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object)
 				$main_object =& $this->Application->recallObject($regs[1]);
 				/* @var $main_object kDBItem */
 
 				$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 				return $main_object->GetDBField($id_field);
 			}
 
 			// 1. get id from post (used in admin)
 			$ret = $this->Application->GetVar($event->getPrefixSpecial(true).'_id');
 			if (($ret !== false) && ($ret != '')) {
 				return $ret;
 			}
 
 			// 2. get id from env (used in front)
 			$ret = $this->Application->GetVar($event->getPrefixSpecial().'_id');
 			if (($ret !== false) && ($ret != '')) {
 				return $ret;
 			}
 
 			// recall selected ids array and use the first one
 			$ids = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
 			if ($ids != '') {
 				$ids = explode(',',$ids);
 				if ($ids) {
 					$ret = array_shift($ids);
 				}
 			}
 			else { // if selected ids are not yet stored
 				$this->StoreSelectedIDs($event);
 				return $this->Application->GetVar($event->getPrefixSpecial().'_id'); // StoreSelectedIDs sets this variable
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Prepares and stores selected_ids string
 		 * in Session and Application Variables
 		 * by getting all checked ids from grid plus
 		 * id passed in get/post as prefix_id
 		 *
 		 * @param kEvent $event
 		 * @param Array $direct_ids
 		 *
 		 * @return Array ids stored
 		 */
 		function StoreSelectedIDs(&$event, $direct_ids = null)
 		{
 			$wid = $this->Application->GetTopmostWid($event->Prefix);
 			$session_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
 
 			$ids = $event->getEventParam('ids');
 			if (isset($direct_ids) || ($ids !== false)) {
 				// save ids directly if they given + reset array indexes
 				$resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false);
 				if ($resulting_ids) {
 					$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids));
 					$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name);
 					$this->Application->SetVar($event->getPrefixSpecial() . '_id', $resulting_ids[0]);
 
 					return $resulting_ids;
 				}
 
 				return Array ();
 			}
 
 			$ret = Array();
 
 			// May be we don't need this part: ?
 			$passed = $this->Application->GetVar($event->getPrefixSpecial(true).'_id');
 			if($passed !== false && $passed != '')
 			{
 				array_push($ret, $passed);
 			}
 
 			$ids = Array();
 
 			// get selected ids from post & save them to session
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info)
 			{
 				$id_field = $this->Application->getUnitOption($event->Prefix,'IDField');
 				foreach($items_info as $id => $field_values)
 				{
 					if( getArrayValue($field_values,$id_field) ) array_push($ids,$id);
 				}
 				//$ids=array_keys($items_info);
 			}
 
 			$ret = array_unique(array_merge($ret, $ids));
 
 			$this->Application->SetVar($event->getPrefixSpecial().'_selected_ids', implode(',',$ret));
 			$this->Application->LinkVar($event->getPrefixSpecial().'_selected_ids', $session_name, '', !$ret); // optional when IDs are missing
 
 			// This is critical - otherwise getPassedID will return last ID stored in session! (not exactly true)
 			// this smells... needs to be refactored
 			$first_id = getArrayValue($ret,0);
 			if (($first_id === false) && ($event->getEventParam('raise_warnings') == 1)) {
 				if ($this->Application->isDebugMode()) {
 					$this->Application->Debugger->appendTrace();
 				}
 				trigger_error('Requested ID for prefix <b>'.$event->getPrefixSpecial().'</b> <span class="debug_error">not passed</span>',E_USER_NOTICE);
 			}
 
 			$this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id);
 			return $ret;
 		}
 
 		/**
 		 * Returns stored selected ids as an array
 		 *
 		 * @param kEvent $event
 		 * @param bool $from_session return ids from session (written, when editing was started)
 		 * @return array
 		 */
 		function getSelectedIDs(&$event, $from_session = false)
 		{
 			if ($from_session) {
 				$wid = $this->Application->GetTopmostWid($event->Prefix);
 				$var_name = rtrim($event->getPrefixSpecial().'_selected_ids_'.$wid, '_');
 				$ret = $this->Application->RecallVar($var_name);
 			}
 			else {
 				$ret = $this->Application->GetVar($event->getPrefixSpecial().'_selected_ids');
 			}
 
 			return explode(',', $ret);
 		}
 
 		/**
 		 * Returs associative array of submitted fields for current item
 		 * Could be used while creating/editing single item -
 		 * meaning on any edit form, except grid edit
 		 *
 		 * @param kEvent $event
 		 */
 		function getSubmittedFields(&$event)
 		{
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			$field_values = $items_info ? array_shift($items_info) : Array();
 			return $field_values;
 		}
 
 		/**
 		 * Removes any information about current/selected ids
 		 * from Application variables and Session
 		 *
 		 * @param kEvent $event
 		 */
 		function clearSelectedIDs(&$event)
 		{
 			$prefix_special = $event->getPrefixSpecial();
 
 			$ids = implode(',', $this->getSelectedIDs($event, true));
 			$event->setEventParam('ids', $ids);
 
 			$wid = $this->Application->GetTopmostWid($event->Prefix);
 			$session_name = rtrim($prefix_special.'_selected_ids_'.$wid, '_');
 
 			$this->Application->RemoveVar($session_name);
 			$this->Application->SetVar($prefix_special.'_selected_ids', '');
 
 			$this->Application->SetVar($prefix_special.'_id', ''); // $event->getPrefixSpecial(true).'_id' too may be
 		}
 
 		/*function SetSaveEvent(&$event)
 		{
 			$this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnUpdate');
 			$this->Application->LinkVar($event->Prefix_Special.'_SaveEvent');
 		}*/
 
 		/**
 		 * Common builder part for Item & List
 		 *
 		 * @param kDBBase $object
 		 * @param kEvent $event
 		 * @access private
 		 */
 		function dbBuild(&$object, &$event)
 		{
 			// for permission checking inside item/list build events
 			$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 
 			$object->Configure( $event->getEventParam('populate_ml_fields') || $this->Application->getUnitOption($event->Prefix, 'PopulateMlFields') );
 			$this->PrepareObject($object, $event);
 
 			// force live table if specified or is original item
 			$live_table = $event->getEventParam('live_table') || $event->Special == 'original';
 
 			if( $this->UseTempTables($event) && !$live_table )
 			{
 				$object->SwitchToTemp();
 			}
 
 			// This strange constuction creates hidden field for storing event name in form submit
 			// It pass SaveEvent to next screen, otherwise after unsuccsefull create it will try to update rather than create
 			$current_event = $this->Application->GetVar($event->Prefix_Special.'_event');
 //			$this->Application->setEvent($event->Prefix_Special, $current_event);
 			$this->Application->setEvent($event->Prefix_Special, '');
 
 			$save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate';
 			$this->Application->SetVar($event->Prefix_Special.'_SaveEvent',$save_event);
 		}
 
 		/**
 		 * Checks, that currently loaded item is allowed for viewing (non permission-based)
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function checkItemStatus(&$event)
 		{
 			$status_fields = $this->Application->getUnitOption($event->Prefix,'StatusField');
 			if (!$status_fields) {
 				return true;
 			}
 
 			$status_field = array_shift($status_fields);
 			if ($status_field == 'Status' || $status_field == 'Enabled') {
 				$object =& $event->getObject();
 				if (!$object->isLoaded()) {
 					return true;
 				}
 
 				return $object->GetDBField($status_field) == STATUS_ACTIVE;
 			}
 			return true;
 		}
 
 		/**
 		 * Shows not found template content
 		 *
 		 * @param kEvent $event
 		 *
 		 */
 		function _errorNotFound(&$event)
 		{
 			if ($event->getEventParam('raise_warnings') === 0) {
 				// when it's possible, that autoload fails do nothing
 				return ;
 			}
 
 			if ($this->Application->isDebugMode()) {
 				$this->Application->Debugger->appendTrace();
 			}
 			trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_NOTICE);
 
 			header('HTTP/1.0 404 Not Found');
 
 			while (ob_get_level()) {
 				ob_end_clean();
 			}
 
 			// object is used inside template parsing, so break out any parsing and return error document
 			$error_template = $this->Application->ConfigValue('ErrorTemplate');
 
 			$themes_helper =& $this->Application->recallObject('ThemesHelper');
 			/* @var $themes_helper kThemesHelper */
 
 			$this->Application->SetVar('t', $error_template);
 			$this->Application->SetVar('m_cat_id', $themes_helper->getPageByTemplate($error_template));
 
 			// in case if missing item is recalled first from event (not from template)
 			$this->Application->InitParser();
 			$this->Application->HTML = $this->Application->ParseBlock( Array ('name' => $error_template) );
 			$this->Application->Done();
 			exit;
 		}
 
 		/**
 		 * Builds item (loads if needed)
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnItemBuild(&$event)
 		{
 			$object =& $event->getObject();
 			$this->dbBuild($object,$event);
 
 			$sql = $this->ItemPrepareQuery($event);
 			$sql = $this->Application->ReplaceLanguageTags($sql);
 			$object->setSelectSQL($sql);
 
 			// 2. loads if allowed
 			$auto_load = $this->Application->getUnitOption($event->Prefix,'AutoLoad');
 			$skip_autload = $event->getEventParam('skip_autoload');
 
 			if ($auto_load && !$skip_autload) {
 				$perm_status = true;
 				$user_id = $this->Application->RecallVar('user_id');
 				$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 				$status_checked = false;
 				if ($user_id == -1 || $this->CheckPermission($event)) {
 					// don't autoload item, when user doesn't have view permission
 					$this->LoadItem($event);
 
 					$status_checked = true;
 					$editing_mode = defined('EDITING_MODE') ? EDITING_MODE : false;
 
 					if ($user_id != -1 && !$this->Application->isAdmin && !($editing_mode || $this->checkItemStatus($event))) {
 						// non-root user AND on front-end AND (not editing mode || incorrect status)
 						$perm_status = false;
 					}
 				}
 				else {
 					$perm_status = false;
 				}
 
 				if (!$perm_status) {
 					// when no permission to view item -> redirect to no pemrission template
 					if ($this->Application->isDebugMode()) {
 						$this->Application->Debugger->appendTrace();
 					}
 
 					trigger_error('ItemLoad Permission Failed for prefix ['.$event->getPrefixSpecial().'] in <strong>'.($status_checked ? 'checkItemStatus' : 'CheckPermission').'</strong>', E_USER_NOTICE);
 					$template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
 
 					if (MOD_REWRITE) {
 						$redirect_params = Array (
 							'm_cat_id' => 0,
 							'next_template' => urlencode('external:' . $_SERVER['REQUEST_URI']),
 						);
 					}
 					else {
 						$redirect_params = Array (
 							'next_template' => $this->Application->GetVar('t'),
 						);
 					}
 
 					$this->Application->Redirect($template, $redirect_params);
 				}
 			}
 
 			$actions =& $this->Application->recallObject('kActions');
 			$actions->Set($event->Prefix_Special.'_GoTab', '');
 
 			$actions->Set($event->Prefix_Special.'_GoId', '');
 		}
 
 		/**
 		 * Build subtables array from configs
 		 *
 		 * @param kEvent $event
 		 */
 		function OnTempHandlerBuild(&$event)
 		{
 			$object =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			/* @var $object kTempTablesHandler */
 
 			$object->BuildTables( $event->Prefix, $this->getSelectedIDs($event) );
 		}
 
 		/**
 		 * Checks, that object used in event should use temp tables
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function UseTempTables(&$event)
 		{
 			$top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual
 			$special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event);
 
 			return $this->Application->IsTempMode($event->Prefix, $special);
 		}
 
 		/**
 		 * Returns table prefix from event (temp or live)
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @todo Needed? Should be refactored (by Alex)
 		 */
 		function TablePrefix(&$event)
 		{
 			return $this->UseTempTables($event) ? $this->Application->GetTempTablePrefix('prefix:'.$event->Prefix).TABLE_PREFIX : TABLE_PREFIX;
 		}
 
 		/**
 		 * Load item if id is available
 		 *
 		 * @param kEvent $event
 		 */
 		function LoadItem(&$event)
 		{
 			$object =& $event->getObject();
 			$id = $this->getPassedID($event);
 
 			if ($object->isLoaded() && !is_array($id) && ($object->GetID() == $id)) {
 				// object is already loaded by same id
 				return ;
 			}
 
 			if ($object->Load($id)) {
 				$actions =& $this->Application->recallObject('kActions');
 				$actions->Set($event->Prefix_Special.'_id', $object->GetID() );
 			}
 			else {
 				$object->setID($id);
 			}
 		}
 
 		/**
 		 * Builds list
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnListBuild(&$event)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			$this->dbBuild($object,$event);
 
 			$sql = $this->ListPrepareQuery($event);
 			$sql = $this->Application->ReplaceLanguageTags($sql);
 			$object->setSelectSQL($sql);
 
 			$object->Counted = false; // when requery="1" should re-count records too!
 			$object->ClearOrderFields(); // prevents duplicate order fields, when using requery="1"
 
 			$object->linkToParent( $this->getMainSpecial($event) );
 
 			$this->AddFilters($event);
 			$this->SetCustomQuery($event);	// new!, use this for dynamic queries based on specials for ex.
 			$this->SetPagination($event);
 			$this->SetSorting($event);
 
 //			$object->CalculateTotals();	 // Now called in getTotals to avoid extra query
 
 			$actions =& $this->Application->recallObject('kActions');
 			$actions->Set('remove_specials['.$event->Prefix_Special.']', '0');
 			$actions->Set($event->Prefix_Special.'_GoTab', '');
 		}
 
 
 		/**
 		 * Get's special of main item for linking with subitem
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function getMainSpecial(&$event)
 		{
 			$main_special = $event->getEventParam('main_special');
 
 			if ($main_special === false) {
 				// main item's special not passed
 
 				if (substr($event->Special, -5) == '-item') {
 					// temp handler added "-item" to given special -> process that here
 					return substr($event->Special, 0, -5);
 				}
 
 				// by default subitem's special is used for main item searching
 				return $event->Special;
 			}
 
 			return $main_special;
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 * @see OnListBuild
 		 */
 		function SetCustomQuery(&$event)
 		{
 
 		}
 
 		/**
 		 * Set's new perpage for grid
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetPerPage(&$event)
 		{
 			$per_page = $this->Application->GetVar($event->getPrefixSpecial(true).'_PerPage');
 			$this->Application->StoreVar($event->getPrefixSpecial().'_PerPage', $per_page);
 
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
 			$this->Application->StorePersistentVar($event->getPrefixSpecial().'_PerPage.'.$view_name, $per_page);
 		}
 
 		/**
 		 * Occurs when page is changed (only for hooking)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetPage(&$event)
 		{
 			$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
 			$event->SetRedirectParam($event->getPrefixSpecial().'_Page', $page);
 		}
 
 		/**
 		 * Set's correct page for list
 		 * based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @access private
 		 * @see OnListBuild
 		 */
 		function SetPagination(&$event)
 		{
 			// get PerPage (forced -> session -> config -> 10)
 			$per_page = $this->getPerPage($event);
 
 			$object =& $event->getObject();
 			$object->SetPerPage($per_page);
 			$this->Application->StoreVarDefault($event->getPrefixSpecial().'_Page', 1, true); // true for optional
 
 			$page = $this->Application->GetVar($event->getPrefixSpecial().'_Page');
 			if (!$page) {
 				$page = $this->Application->GetVar($event->getPrefixSpecial(true).'_Page');
 			}
 			if (!$page) {
 				$page = $this->Application->RecallVar($event->getPrefixSpecial().'_Page');
 			}
 			else {
 				$this->Application->StoreVar($event->getPrefixSpecial().'_Page', $page);
 			}
 
 			if( !$event->getEventParam('skip_counting') )
 			{
 				$pages = $object->GetTotalPages();
 				if($page > $pages)
 				{
 					$this->Application->StoreVar($event->getPrefixSpecial().'_Page', 1);
 					$page = 1;
 				}
 			}
 
 			/*$per_page = $event->getEventParam('per_page');
 			if ($per_page == 'list_next') {
 
 				$cur_page = $page;
 				$cur_per_page = $per_page;
 
 				$object->SetPerPage(1);
 
 				$object =& $this->Application->recallObject($event->Prefix);
 				$cur_item_index = $object->CurrentIndex;
 
 				$page = ($cur_page-1) * $cur_per_page + $cur_item_index + 1;
 				$object->SetPerPage(1);
 			}*/
 
 			$object->SetPage($page);
 		}
 
 		/**
 		 * Returns current per-page setting for list
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 */
 		function getPerPage(&$event)
 		{
 			// 1. per-page is passed as tag parameter to PrintList, InitList, etc.
 			$per_page = $event->getEventParam('per_page');
 
 			/*if ($per_page == 'list_next') {
 				$per_page = '';
 			}*/
 
 			// 2. per-page variable name is store into config variable
 			$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 			if ($config_mapping) {
 				switch ( $per_page ){
 					case 'short_list' :
 						$per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']);
 						break;
 					case 'default' :
 						$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
 						break;
 				}
 			}
 
 			if (!$per_page) {
 				// per-page is stored to persistent session
 				$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
 
 				$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->getPrefixSpecial();
 				$per_page = $this->Application->RecallPersistentVar($storage_prefix.'_PerPage.'.$view_name, ALLOW_DEFAULT_SETTINGS);
 
 				if (!$per_page) {
 					// per-page is stored to current session
 					$per_page = $this->Application->RecallVar($storage_prefix.'_PerPage');
 				}
 
 				if (!$per_page) {
 					if ($config_mapping) {
 						if (!isset($config_mapping['PerPage'])) {
 							trigger_error('Incorrect mapping of <span class="debug_error">PerPage</span> key in config for prefix <b>'.$event->Prefix.'</b>', E_USER_WARNING);
 						}
 						$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
 					}
 					if (!$per_page) {
 						// none of checked above per-page locations are useful, then try default value
 						$default_per_page = $event->getEventParam('default_per_page');
 						$per_page = is_numeric($default_per_page) ? $default_per_page : 10;
 					}
 				}
 			}
 
 			return $per_page;
 		}
 
 		/**
 		 * Set's correct sorting for list
 		 * based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @access private
 		 * @see OnListBuild
 		 */
 		function SetSorting(&$event)
 		{
 			$event->setPseudoClass('_List');
 			$object =& $event->getObject();
 
 			$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->Prefix_Special;
 			$cur_sort1		=	$this->Application->RecallVar($storage_prefix.'_Sort1');
 			$cur_sort1_dir	=	$this->Application->RecallVar($storage_prefix.'_Sort1_Dir');
 			$cur_sort2		=	$this->Application->RecallVar($storage_prefix.'_Sort2');
 			$cur_sort2_dir	=	$this->Application->RecallVar($storage_prefix.'_Sort2_Dir');
 
 			$sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 			$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings');
 			$sorting_prefix = array_key_exists($event->Special, $list_sortings) ? $event->Special : '';
 
 			$tag_sort_by = $event->getEventParam('sort_by');
 			if ($tag_sort_by) {
 				if ($tag_sort_by == 'random') {
 					$object->AddOrderField('RAND()', '');
 				}
 				else {
 					$tag_sort_by = explode('|', $tag_sort_by);
 					foreach ($tag_sort_by as $sorting_element) {
 						list ($by, $dir) = explode(',', $sorting_element);
 						$object->AddOrderField($by, $dir);
 					}
 				}
 			}
 
 			if ($sorting_configs && isset ($sorting_configs['DefaultSorting1Field'])){
 				$list_sortings[$sorting_prefix]['Sorting'] = Array(
 					$this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']),
 					$this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']),
 				);
 			}
 
 			// Use default if not specified
 			if ( !$cur_sort1 || !$cur_sort1_dir)
 			{
 				if ( $sorting = getArrayValue($list_sortings, $sorting_prefix, 'Sorting') ) {
 					reset($sorting);
 					$cur_sort1 = key($sorting);
 					$cur_sort1_dir = current($sorting);
 					if (next($sorting)) {
 						$cur_sort2 =	key($sorting);
 						$cur_sort2_dir =	current($sorting);
 					}
 				}
 			}
 
 			if ( $forced_sorting = getArrayValue($list_sortings, $sorting_prefix, 'ForcedSorting') ) {
 				foreach ($forced_sorting as $field => $dir) {
 					$object->AddOrderField($field, $dir);
 				}
 			}
 
 			if($cur_sort1 != '' && $cur_sort1_dir != '')
 			{
 				$object->AddOrderField($cur_sort1, $cur_sort1_dir);
 			}
 			if($cur_sort2 != '' && $cur_sort2_dir != '')
 			{
 				$object->AddOrderField($cur_sort2, $cur_sort2_dir);
 			}
 		}
 
 		/**
 		 * Add filters found in session
 		 *
 		 * @param kEvent $event
 		 */
 		function AddFilters(&$event)
 		{
 			$object =& $event->getObject();
 
 			$edit_mark = rtrim($this->Application->GetSID().'_'.$this->Application->GetTopmostWid($event->Prefix), '_');
 
 			// add search filter
 			$filter_data = $this->Application->RecallVar($event->getPrefixSpecial().'_search_filter');
 			if ($filter_data) {
 				$filter_data = unserialize($filter_data);
 				foreach ($filter_data as $filter_field => $filter_params) {
 					$filter_type = ($filter_params['type'] == 'having') ? HAVING_FILTER : WHERE_FILTER;
 					$filter_value = str_replace(EDIT_MARK, $edit_mark, $filter_params['value']);
 					$object->addFilter($filter_field, $filter_value, $filter_type, FLT_SEARCH);
 				}
 			}
 
 			// add custom filter
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
 			$custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_custom_filter.'.$view_name);
 			if ($custom_filters) {
 				$grid_name = $event->getEventParam('grid');
 				$custom_filters = unserialize($custom_filters);
 				if (isset($custom_filters[$grid_name])) {
 					foreach ($custom_filters[$grid_name] as $field_name => $field_options) {
 						list ($filter_type, $field_options) = each($field_options);
 						if (isset($field_options['value']) && $field_options['value']) {
 							$filter_type = ($field_options['sql_filter_type'] == 'having') ? HAVING_FILTER : WHERE_FILTER;
 							$filter_value = str_replace(EDIT_MARK, $edit_mark, $field_options['value']);
 							$object->addFilter($field_name, $filter_value, $filter_type, FLT_CUSTOM);
 						}
 					}
 				}
 			}
 
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
 			if($view_filter)
 			{
 				$view_filter = unserialize($view_filter);
 				$temp_filter =& $this->Application->makeClass('kMultipleFilter');
 				$filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu');
 
 				$group_key = 0; $group_count = count($filter_menu['Groups']);
 				while($group_key < $group_count)
 				{
 					$group_info = $filter_menu['Groups'][$group_key];
 
 					$temp_filter->setType( constant('FLT_TYPE_'.$group_info['mode']) );
 					$temp_filter->clearFilters();
 					foreach ($group_info['filters'] as $flt_id)
 					{
 						$sql_key = getArrayValue($view_filter,$flt_id) ? 'on_sql' : 'off_sql';
 						if ($filter_menu['Filters'][$flt_id][$sql_key] != '')
 						{
 							$temp_filter->addFilter('view_filter_'.$flt_id, $filter_menu['Filters'][$flt_id][$sql_key]);
 						}
 					}
 					$object->addFilter('view_group_'.$group_key, $temp_filter, $group_info['type'] , FLT_VIEW);
 					$group_key++;
 				}
 			}
 		}
 
 		/**
 		 * Set's new sorting for list
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnSetSorting(&$event)
 		{
 			$cur_sort1		=	$this->Application->RecallVar($event->Prefix_Special.'_Sort1');
 			$cur_sort1_dir	=	$this->Application->RecallVar($event->Prefix_Special.'_Sort1_Dir');
 
 			$use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting');
 
 			if ($use_double_sorting) {
 				$cur_sort2		=	$this->Application->RecallVar($event->Prefix_Special.'_Sort2');
 				$cur_sort2_dir	=	$this->Application->RecallVar($event->Prefix_Special.'_Sort2_Dir');
 			}
 
 			$passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true).'_Sort1');
 			if ($cur_sort1 == $passed_sort1) {
 				$cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc';
 			}
 			else {
 				if ($use_double_sorting) {
 					$cur_sort2 = $cur_sort1;
 					$cur_sort2_dir = $cur_sort1_dir;
 				}
 				$cur_sort1 = $passed_sort1;
 				$cur_sort1_dir = 'asc';
 			}
 
 			$this->Application->StoreVar($event->Prefix_Special.'_Sort1', $cur_sort1);
 			$this->Application->StoreVar($event->Prefix_Special.'_Sort1_Dir', $cur_sort1_dir);
 			if ($use_double_sorting) {
 				$this->Application->StoreVar($event->Prefix_Special.'_Sort2', $cur_sort2);
 				$this->Application->StoreVar($event->Prefix_Special.'_Sort2_Dir', $cur_sort2_dir);
 			}
 		}
 
 		/**
 		 * Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetSortingDirect(&$event)
 		{
 			$combined = $this->Application->GetVar($event->Prefix.'_CombinedSorting');
 			if ($combined) {
 				list($field, $dir) = explode('|', $combined);
 				$this->Application->StoreVar($event->Prefix.'_Sort1', $field);
 				$this->Application->StoreVar($event->Prefix.'_Sort1_Dir', $dir);
 				return ;
 			}
 
 			$field_pos = $this->Application->GetVar($event->Prefix.'_SortPos');
 			$this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos, $event->Prefix.'_Sort'.$field_pos);
 			$this->Application->LinkVar($event->Prefix.'_Sort'.$field_pos.'_Dir', $event->Prefix.'_Sort'.$field_pos.'_Dir');
 		}
 
 		/**
 		 * Reset grid sorting to default (from config)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnResetSorting(&$event)
 		{
 			$this->Application->RemoveVar($event->Prefix_Special.'_Sort1');
 			$this->Application->RemoveVar($event->Prefix_Special.'_Sort1_Dir');
 			$this->Application->RemoveVar($event->Prefix_Special.'_Sort2');
 			$this->Application->RemoveVar($event->Prefix_Special.'_Sort2_Dir');
 		}
 
 		/**
 		 * Sets grid refresh interval
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetAutoRefreshInterval(&$event)
 		{
 			$refresh_interval = $this->Application->GetVar('refresh_interval');
 
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
 			$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_interval);
 		}
 
 		/**
 		 * Changes auto-refresh state for grid
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAutoRefreshToggle(&$event)
 		{
 			$refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals');
 			if (!$refresh_intervals) {
 				return ;
 			}
 
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial().'_current_view');
 			$auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name);
 
 			if ($auto_refresh === false) {
 				$refresh_intervals = explode(',', $refresh_intervals);
 				$this->Application->StorePersistentVar($event->getPrefixSpecial().'_refresh_interval.'.$view_name, $refresh_intervals[0]);
 			}
 
 			$this->Application->StorePersistentVar($event->getPrefixSpecial().'_auto_refresh.'.$view_name, $auto_refresh ? 0 : 1);
 		}
 
 		/**
 		 * Creates needed sql query to load item,
 		 * if no query is defined in config for
 		 * special requested, then use default
 		 * query
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function ItemPrepareQuery(&$event)
 		{
 			$sqls = $this->Application->getUnitOption($event->Prefix, 'ItemSQLs', Array ());
 			$special = array_key_exists($event->Special, $sqls) ? $event->Special : '';
 
 			if (!array_key_exists($special, $sqls)) {
 				// preferred special not found in ItemSQLs -> use analog from ListSQLs
 				return $this->ListPrepareQuery($event);
 			}
 
 			return $sqls[$special];
 		}
 
 		/**
 		 * Creates needed sql query to load list,
 		 * if no query is defined in config for
 		 * special requested, then use default
 		 * query
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function ListPrepareQuery(&$event)
 		{
 			$sqls = $this->Application->getUnitOption($event->Prefix, 'ListSQLs', Array ());
 			return $sqls[ array_key_exists($event->Special, $sqls) ? $event->Special : '' ];
 		}
 
 		/**
 		 * Apply custom processing to item
 		 *
 		 * @param kEvent $event
 		 */
 		function customProcessing(&$event, $type)
 		{
 
 		}
 
 		/* Edit Events mostly used in Admin */
 
 		/**
 		 * Creates new kDBItem
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnCreate(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if ($items_info) {
 				list($id,$field_values) = each($items_info);
 				$object->SetFieldsFromHash($field_values);
 			}
 
 			$this->customProcessing($event,'before');
 
 			//look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default
-			if( $object->Create($event->getEventParam('ForceCreateId')) )
-			{
-				if( $object->IsTempTable() ) $object->setTempID();
+			if ( $object->Create($event->getEventParam('ForceCreateId')) ) {
 				$this->customProcessing($event,'after');
 				$event->status=erSUCCESS;
 				$event->redirect_params = Array('opener'=>'u');
 			}
-			else
-			{
+			else {
 				$event->status = erFAIL;
 				$event->redirect = false;
 				$this->Application->SetVar($event->Prefix_Special.'_SaveEvent','OnCreate');
 				$object->setID($id);
 			}
 		}
 
 		/**
 		 * Updates kDBItem
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnUpdate(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if ($items_info) {
 				foreach ($items_info as $id => $field_values) {
 					$object->Load($id);
 	 				$object->SetFieldsFromHash($field_values);
 	 				$this->customProcessing($event, 'before');
 
 					if ( $object->Update($id) ) {
 						$this->customProcessing($event, 'after');
 						$event->status = erSUCCESS;
 					}
 					else {
 						$event->status = erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Delete's kDBItem object
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnDelete(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return;
 			}
 
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			/* @var $temp kTempTablesHandler */
 
 			$temp->DeleteItems($event->Prefix, $event->Special, Array($this->getPassedID($event)));
 		}
 
 		/**
 		 * Deletes all records from table
 		 *
 		 * @param kEvent $event
 		 */
 		function OnDeleteAll(&$event)
 		{
 			$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName');
 			$ids = $this->Conn->GetCol($sql);
 
 			if ($ids) {
 				$temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
 				/* @var $temp_handler kTempTablesHandler */
 
 				$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
 			}
 		}
 
 		/**
 		 * Prepares new kDBItem object
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnNew(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			$object->Clear(0);
 			$this->Application->SetVar($event->Prefix_Special.'_SaveEvent', 'OnCreate');
 
 			if ($event->getEventParam('top_prefix') != $event->Prefix) {
 				// this is subitem prefix, so use main item special
 				$table_info = $object->getLinkedInfo( $this->getMainSpecial($event) );
 			}
 			else {
 				$table_info = $object->getLinkedInfo();
 			}
 
 			$object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']);
 
 			$event->redirect = false;
 		}
 
 		/**
 		 * Cancel's kDBItem Editing/Creation
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnCancel(&$event)
 		{
 			$object =& $event->getObject(Array('skip_autoload' => true));
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 			if ($items_info) {
 				$delete_ids = Array();
 				$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 				foreach ($items_info as $id => $field_values) {
 					$object->Load($id);
 					// record created for using with selector (e.g. Reviews->Select User), and not validated => Delete it
 					if ($object->isLoaded() && !$object->Validate() && ($id <= 0) ) {
 						$delete_ids[] = $id;
 					}
 				}
 
 				if ($delete_ids) {
 					$temp->DeleteItems($event->Prefix, $event->Special, $delete_ids);
 				}
 			}
 
 			$event->redirect_params = Array('opener'=>'u');
 		}
 
 
 		/**
 		 * Deletes all selected items.
 		 * Automatically recurse into sub-items using temp handler, and deletes sub-items
 		 * by calling its Delete method if sub-item has AutoDelete set to true in its config file
 		 *
 		 * @param kEvent $event
 		 */
 		function OnMassDelete(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return;
 			}
 
 			$event->status=erSUCCESS;
 
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			$event->setEventParam('ids', $ids);
 			$this->customProcessing($event, 'before');
 			$ids = $event->getEventParam('ids');
 
 			if($ids)
 			{
 				$temp->DeleteItems($event->Prefix, $event->Special, $ids);
 			}
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Sets window id (of first opened edit window) to temp mark in uls
 		 *
 		 * @param kEvent $event
 		 */
 		function setTempWindowID(&$event)
 		{
 			$prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true));
 
 			foreach ($prefixes as $prefix) {
 				$mode = $this->Application->GetVar($prefix . '_mode');
 
 				if ($mode == 't') {
 					$wid = $this->Application->GetVar('m_wid');
 					$this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid);
 					break;
 				}
 			}
 		}
 
 		/**
 		 * Prepare temp tables and populate it
 		 * with items selected in the grid
 		 *
 		 * @param kEvent $event
 		 */
 		function OnEdit(&$event)
 		{
 			$this->setTempWindowID($event);
 			$ids = $this->StoreSelectedIDs($event);
 			$var_name = $event->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
 			$this->Application->RemoveVar($var_name);
 
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			/* @var $temp kTempTablesHandler */
 
 			$temp->PrepareEdit();
 
 			$event->SetRedirectParam('m_lang', $this->Application->GetDefaultLanguageId());
 			$event->SetRedirectParam($event->getPrefixSpecial() . '_id', array_shift($ids));
 			$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
 		}
 
 		/**
 		 * Saves content of temp table into live and
 		 * redirects to event' default redirect (normally grid template)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSave(&$event)
 		{
 			$event->CallSubEvent('OnPreSave');
 			if ($event->status == erSUCCESS) {
 				$skip_master = false;
 				$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 
 				$changes_var_name = $this->Prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
 
 				if (!$this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 					$live_ids = $temp->SaveEdit($event->getEventParam('master_ids') ? $event->getEventParam('master_ids') : Array());
 					if ($live_ids === false) {
 						// coping from table failed, because we have another coping process to same table, that wasn't finished
 						$event->status = erFAIL;
 						return ;
 					}
 
 					// Deleteing files scheduled for delete
 					$var_name = $event->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
 					$schedule = $this->Application->RecallVar($var_name);
 					$schedule = $schedule ? unserialize($schedule) : array();
 					foreach ($schedule as $data) {
 						if ($data['action'] == 'delete') {
 							unlink($data['file']);
 						}
 					}
 
 					if ($live_ids) {
 						// ensure, that newly created item ids are avalable as if they were selected from grid
 						// NOTE: only works if main item has subitems !!!
 						$this->StoreSelectedIDs($event, $live_ids);
 					}
 
 					$this->SaveLoggedChanges($changes_var_name);
 				}
 				else {
 					$this->Application->RemoveVar($changes_var_name);
 					$event->status = erFAIL;
 				}
 
 				$this->clearSelectedIDs($event);
 
 				$event->redirect_params = Array('opener' => 'u');
 				$this->Application->RemoveVar($event->getPrefixSpecial().'_modified');
 
 				// all temp tables are deleted here => all after hooks should think, that it's live mode now
 				$this->Application->SetVar($event->Prefix.'_mode', '');
 			}
 		}
 
 		function SaveLoggedChanges($changes_var_name)
 		{
 			$ses_log_id = $this->Application->RecallVar('_SessionLogId_');
 			if (!$ses_log_id) {
 				return ;
 			}
 
 			$changes = $this->Application->RecallVar($changes_var_name);
 			$changes = $changes ? unserialize($changes) : Array ();
 			if (!$changes) {
 				return ;
 			}
 
 			$add_fields = Array (
 				'PortalUserId' => $this->Application->RecallVar('user_id'),
 				'SessionLogId' => $ses_log_id,
 			);
 
 			$changelog_table = $this->Application->getUnitOption('change-log', 'TableName');
 			$sessionlog_table = $this->Application->getUnitOption('session-log', 'TableName');
 
 			foreach ($changes as $rec) {
 				$this->Conn->doInsert(array_merge($rec, $add_fields), $changelog_table);
 			}
 
 			$sql = 'UPDATE '.$sessionlog_table.'
 					SET AffectedItems = AffectedItems + '.count($changes).'
 					WHERE SessionLogId = '.$ses_log_id;
 			$this->Conn->Query($sql);
 
 			$this->Application->RemoveVar($changes_var_name);
 		}
 
 
 		/**
 		 * Cancels edit
 		 * Removes all temp tables and clears selected ids
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCancelEdit(&$event)
 		{
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			$temp->CancelEdit();
 
 			$this->clearSelectedIDs($event);
 			$event->redirect_params = Array('opener'=>'u');
 			$this->Application->RemoveVar($event->getPrefixSpecial().'_modified');
 		}
 
 
 		/**
 		 * Allows to determine if we are creating new item or editing already created item
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function isNewItemCreate(&$event)
 		{
 			$object =& $event->getObject( Array ('raise_warnings' => 0) );
 			return !$object->IsLoaded();
 
 //			$item_id = $this->getPassedID($event);
 //			return ($item_id == '') ? true : false;
 		}
 
 		/**
 		 * Saves edited item into temp table
 		 * If there is no id, new item is created in temp table
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSave(&$event)
 		{
 			//$event->redirect = false;
 			// if there is no id - it means we need to create an item
 			if (is_object($event->MasterEvent)) {
 				$event->MasterEvent->setEventParam('IsNew',false);
 			}
 
 			if ($this->isNewItemCreate($event)) {
 				$event->CallSubEvent('OnPreSaveCreated');
 				if (is_object($event->MasterEvent)) {
 					$event->MasterEvent->setEventParam('IsNew',true);
 				}
 				return;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if ($items_info) {
 				foreach ($items_info as $id => $field_values) {
 					$object->SetDefaultValues();
 					$object->Load($id);
 	 				$object->SetFieldsFromHash($field_values);
 	 				$this->customProcessing($event, 'before');
 					if( $object->Update($id) )
 					{
 						$this->customProcessing($event, 'after');
 						$event->status=erSUCCESS;
 					}
 					else {
 						$event->status = erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 		}
 
 		/**
 		 * [HOOK] Saves subitem
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveSubItem(&$event)
 		{
 			$not_created = $this->isNewItemCreate($event);
 
 			$event->CallSubEvent($not_created ? 'OnCreate' : 'OnUpdate');
 			if ($event->status == erSUCCESS) {
 				$object =& $event->getObject();
 				/* @var $object kDBItem */
 
 				$this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID());
 			}
 			else {
 				$event->MasterEvent->status = $event->status;
 			}
 
 			$event->SetRedirectParam('opener', 's');
 		}
 
 		/**
 		 * Saves edited item in temp table and loads
 		 * item with passed id in current template
 		 * Used in Prev/Next buttons
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveAndGo(&$event)
 		{
 			$event->CallSubEvent('OnPreSave');
 
 			if ($event->status == erSUCCESS) {
 				$id = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId');
 				$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
 			}
 		}
 
 		/**
 		 * Saves edited item in temp table and goes
 		 * to passed tabs, by redirecting to it with OnPreSave event
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveAndGoToTab(&$event)
 		{
 			$event->CallSubEvent('OnPreSave');
 			if ($event->status==erSUCCESS) {
 				$event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab');
 			}
 		}
 
 		/**
 		 * Saves editable list and goes to passed tab,
 		 * by redirecting to it with empty event
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUpdateAndGoToTab(&$event)
 		{
 			$event->setPseudoClass('_List');
 			$event->CallSubEvent('OnUpdate');
 			if ($event->status==erSUCCESS) {
 				$event->redirect=$this->Application->GetVar($event->getPrefixSpecial(true).'_GoTab');
 			}
 		}
 
 		/**
 		 * Prepare temp tables for creating new item
 		 * but does not create it. Actual create is
 		 * done in OnPreSaveCreated
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreCreate(&$event)
 		{
 			$this->setTempWindowID($event);
 			$this->clearSelectedIDs($event);
 			$this->Application->SetVar('m_lang', $this->Application->GetDefaultLanguageId());
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$temp =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
 			$temp->PrepareEdit();
 
 			$object->setID(0);
 			$this->Application->SetVar($event->getPrefixSpecial().'_id', 0);
 			$this->Application->SetVar($event->getPrefixSpecial().'_PreCreate', 1);
 
 			$event->redirect = false;
 		}
 
 		/**
 		 * Creates a new item in temp table and
 		 * stores item id in App vars and Session on succsess
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveCreated(&$event)
 		{
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info) $field_values = array_shift($items_info);
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$object->SetFieldsFromHash($field_values);
 
 			$this->customProcessing($event, 'before');
 
 			if( $object->Create() )
 			{
 				$this->customProcessing($event, 'after');
 				$event->redirect_params[$event->getPrefixSpecial(true).'_id'] = $object->GetId();
 				$event->status=erSUCCESS;
 			}
 			else
 			{
 				$event->status=erFAIL;
 				$event->redirect=false;
 				$object->setID(0);
 			}
 
 		}
 
 		function OnReset(&$event)
 		{
 			//do nothing - should reset :)
 			if ($this->isNewItemCreate($event)) {
 				// just reset id to 0 in case it was create
 				$object =& $event->getObject( Array('skip_autoload' => true) );
 				$object->setID(0);
 				$this->Application->SetVar($event->getPrefixSpecial().'_id',0);
 			}
 		}
 
 		/**
 		 * Apply same processing to each item beeing selected in grid
 		 *
 		 * @param kEvent $event
 		 * @access private
 		 */
 		function iterateItems(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return;
 			}
 
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 			$ids = $this->StoreSelectedIDs($event);
 
 			if ($ids) {
 				$status_field = array_shift( $this->Application->getUnitOption($event->Prefix,'StatusField') );
 				$order_field = $this->Application->getUnitOption($event->Prefix,'OrderField');
 
 				if (!$order_field) {
 					$order_field = 'Priority';
 				}
 
 				foreach ($ids as $id) {
 					$object->Load($id);
 
 					switch ($event->Name) {
 						case 'OnMassApprove':
 							$object->SetDBField($status_field, 1);
 							break;
 
 						case 'OnMassDecline':
 							$object->SetDBField($status_field, 0);
 							break;
 
 						case 'OnMassMoveUp':
 							$object->SetDBField($order_field, $object->GetDBField($order_field) + 1);
 							break;
 
 						case 'OnMassMoveDown':
 							$object->SetDBField($order_field, $object->GetDBField($order_field) - 1);
 							break;
 					}
 
 					if ($object->Update()) {
 						$event->status = erSUCCESS;
 					}
 					else {
 						$event->status = erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnMassClone(&$event)
 		{
 			if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
 				$event->status = erFAIL;
 				return;
 			}
 
 			$event->status = erSUCCESS;
 
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			if ($ids) {
 				$temp->CloneItems($event->Prefix, $event->Special, $ids);
 			}
 
 			$this->clearSelectedIDs($event);
 		}
 
 		function check_array($records, $field, $value)
 		{
 			foreach ($records as $record) {
 				if ($record[$field] == $value) {
 					return true;
 				}
 			}
 			return false;
 		}
 
 		function OnPreSavePopup(&$event)
 		{
 			$object =& $event->getObject();
 			$this->RemoveRequiredFields($object);
 			$event->CallSubEvent('OnPreSave');
 
 			$this->finalizePopup($event);
 		}
 
 
 /* End of Edit events */
 
 		// III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item
 
 		/**
 		 * Occurse before loading item, 'id' parameter
 		 * allows to get id of item beeing loaded
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnBeforeItemLoad(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse after loading item, 'id' parameter
 		 * allows to get id of item that was loaded
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnAfterItemLoad(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse before creating item
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnBeforeItemCreate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse after creating item
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnAfterItemCreate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse before updating item
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnBeforeItemUpdate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse after updating item
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse before deleting item, id of item beeing
 		 * deleted is stored as 'id' event param
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnBeforeItemDelete(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurse after deleting item, id of deleted item
 		 * is stored as 'id' param of event
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function OnAfterItemDelete(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurs before validation attempt
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemValidate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occurs after successful item validation
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemValidate(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures after an item has been copied to temp
 		 * Id of copied item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterCopyToTemp(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures before an item is deleted from live table when copying from temp
 		 * (temp handler deleted all items from live and then copy over all items from temp)
 		 * Id of item being deleted is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeDeleteFromLive(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures before an item is copied to live table (after all foreign keys have been updated)
 		 * Id of item being copied is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeCopyToLive(&$event)
 		{
 
 		}
 
 		/**
 		 * !!! NOT FULLY IMPLEMENTED - SEE TEMP HANDLER COMMENTS (search by event name)!!!
 		 * Occures after an item has been copied to live table
 		 * Id of copied item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterCopyToLive(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures before an item is cloneded
 		 * Id of ORIGINAL item is passed as event' 'id' param
 		 * Do not call object' Update method in this event, just set needed fields!
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeClone(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures after an item has been cloned
 		 * Id of newly created item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterClone(&$event)
 		{
 
 		}
 
 		/**
 		 * Occures after list is queried
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterListQuery(&$event)
 		{
 
 		}
 
 		/**
 		 * Ensures that popup will be closed automatically
 		 * and parent window will be refreshed with template
 		 * passed
 		 *
 		 * @param kEvent $event
 		 * @access public
 		 */
 		function finalizePopup(&$event)
 		{
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Create search filters based on search query
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnSearch(&$event)
 		{
 			$event->setPseudoClass('_List');
 
 			$search_helper =& $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$search_helper->performSearch($event);
 		}
 
 		/**
 		 * Clear search keywords
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function OnSearchReset(&$event)
 		{
 			$search_helper =& $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$search_helper->resetSearch($event);
 		}
 
 		/**
 		 * Set's new filter value (filter_id meaning from config)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSetFilter(&$event)
 		{
 			$filter_id = $this->Application->GetVar('filter_id');
 			$filter_value = $this->Application->GetVar('filter_value');
 
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
 			$view_filter = $view_filter ? unserialize($view_filter) : Array();
 
 			$view_filter[$filter_id] = $filter_value;
 
 			$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
 		}
 
 		function OnSetFilterPattern(&$event)
 		{
 			$filters = $this->Application->GetVar($event->getPrefixSpecial(true).'_filters');
 			if (!$filters) return ;
 
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial().'_view_filter');
 			$view_filter = $view_filter ? unserialize($view_filter) : Array();
 
 			$filters = explode(',', $filters);
 			foreach ($filters as $a_filter) {
 				list($id, $value) = explode('=', $a_filter);
 				$view_filter[$id] = $value;
 			}
 			$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
 			$event->redirect = false;
 		}
 
 		/**
 		 * Add/Remove all filters applied to list from "View" menu
 		 *
 		 * @param kEvent $event
 		 */
 		function FilterAction(&$event)
 		{
 			$view_filter = Array();
 			$filter_menu = $this->Application->getUnitOption($event->Prefix,'FilterMenu');
 			switch ($event->Name)
 			{
 				case 'OnRemoveFilters':
 					$filter_value = 1;
 					break;
 
 				case 'OnApplyFilters':
 					$filter_value = 0;
 					break;
 			}
 
 			foreach($filter_menu['Filters'] as $filter_key => $filter_params)
 			{
 				if(!$filter_params) continue;
 				$view_filter[$filter_key] = $filter_value;
 			}
 			$this->Application->StoreVar( $event->getPrefixSpecial().'_view_filter', serialize($view_filter) );
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveAndOpenTranslator(&$event)
 		{
 			$this->Application->SetVar('allow_translation', true);
 			$object =& $event->getObject();
 			$this->RemoveRequiredFields($object);
 			$event->CallSubEvent('OnPreSave');
 
 			if ($event->status == erSUCCESS) {
 
 				$resource_id = $this->Application->GetVar('translator_resource_id');
 				if ($resource_id) {
 					$t_prefixes = explode(',', $this->Application->GetVar('translator_prefixes'));
 
 					$cdata =& $this->Application->recallObject($t_prefixes[1], null, Array('skip_autoload' => true));
 					$cdata->Load($resource_id, 'ResourceId');
 					if (!$cdata->isLoaded()) {
 						$cdata->SetDBField('ResourceId', $resource_id);
 						$cdata->Create();
 					}
 					$this->Application->SetVar($cdata->getPrefixSpecial().'_id', $cdata->GetID());
 				}
 
 				$event->redirect = $this->Application->GetVar('translator_t');
 				$event->redirect_params = Array (
 					'pass' => 'all,trans,' . $this->Application->GetVar('translator_prefixes'),
 					'opener' => 's',
 					$event->getPrefixSpecial(true) . '_id' => $object->GetID(),
 					'trans_event'		=>	'OnLoad',
 					'trans_prefix'		=>	$this->Application->GetVar('translator_prefixes'),
 					'trans_field' 		=>	$this->Application->GetVar('translator_field'),
 					'trans_multi_line'	=>	$this->Application->GetVar('translator_multi_line'),
 				);
 
 				// 1. SAVE LAST TEMPLATE TO SESSION (really needed here, because of tweaky redirect)
 				$last_template = $this->Application->RecallVar('last_template');
 				preg_match('/index4\.php\|'.$this->Application->GetSID().'-(.*):/U', $last_template, $rets);
 				$this->Application->StoreVar('return_template', $this->Application->GetVar('t'));
 			}
 		}
 
 		function RemoveRequiredFields(&$object)
 		{
 			// making all field non-required to achieve successful presave
 			foreach($object->Fields as $field => $options)
 			{
 				if(isset($options['required']))
 				{
 					unset($object->Fields[$field]['required']);
 				}
 			}
 		}
 
 		/**
 		 * Saves selected user in needed field
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSelectUser(&$event)
 		{
 			$items_info = $this->Application->GetVar('u');
 			if ($items_info) {
 				$user_id = array_shift( array_keys($items_info) );
 
 				$object =& $event->getObject();
 				$this->RemoveRequiredFields($object);
 
 				$is_new = !$object->isLoaded();
 				$is_main = substr($this->Application->GetVar($event->Prefix.'_mode'), 0, 1) == 't';
 
 				if ($is_new) {
 					$new_event = $is_main ? 'OnPreCreate' : 'OnNew';
 					$event->CallSubEvent($new_event);
 					$event->redirect = true;
 				}
 
 				$object->SetDBField($this->Application->RecallVar('dst_field'), $user_id);
 
 				if ($is_new) {
 					$object->Create();
-					if (!$is_main && $object->IsTempTable()) {
-						$object->setTempID();
-					}
 				}
 				else {
 					$object->Update();
 				}
 			}
 
 			$event->SetRedirectParam($event->getPrefixSpecial().'_id', $object->GetID());
 			$this->finalizePopup($event);
 		}
 
 
 /** EXPORT RELATED **/
 
 		/**
 		 * Shows export dialog
 		 *
 		 * @param kEvent $event
 		 */
 		function OnExport(&$event)
 		{
 			$selected_ids = $this->StoreSelectedIDs($event);
 
 			if (implode(',', $selected_ids) == '') {
 				// K4 fix when no ids found bad selected ids array is formed
 				$selected_ids = false;
 			}
 
 			$this->Application->StoreVar($event->Prefix.'_export_ids', $selected_ids ? implode(',', $selected_ids) : '' );
 
 			$this->Application->LinkVar('export_finish_t');
 			$this->Application->LinkVar('export_progress_t');
 			$this->Application->StoreVar('export_oroginal_special', $event->Special);
 
 			$export_helper =& $this->Application->recallObject('CatItemExportHelper');
 
 			/*list ($index_file, $env) = explode('|', $this->Application->RecallVar('last_template'));
 			$finish_url = $this->Application->BaseURL('/admin').$index_file.'?'.ENV_VAR_NAME.'='.$env;
 			$this->Application->StoreVar('export_finish_url', $finish_url);*/
 
 			$redirect_params = Array (
 				$this->Prefix . '.export_event' => 'OnNew',
 				'pass' => 'all,' . $this->Prefix . '.export'
 			);
 
 			$event->setRedirectParams($redirect_params);
 		}
 
 		/**
 		 * Apply some special processing to
 		 * object beeing recalled before using
 		 * it in other events that call prepareObject
 		 *
 		 * @param Object $object
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		function prepareObject(&$object, &$event)
 		{
 			if ($event->Special == 'export' || $event->Special == 'import')
 			{
 				$export_helper =& $this->Application->recallObject('CatItemExportHelper');
 				/* @var $export_helper kCatDBItemExportHelper */
 
 				$export_helper->prepareExportColumns($event);
 			}
 		}
 
 		/**
 		 * Returns specific to each item type columns only
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 */
 		function getCustomExportColumns(&$event)
 		{
 			return Array();
 		}
 
 		/**
 		 * Export form validation & processing
 		 *
 		 * @param kEvent $event
 		 */
 		function OnExportBegin(&$event)
 		{
 			$export_helper =& $this->Application->recallObject('CatItemExportHelper');
 			/* @var $export_helper kCatDBItemExportHelper */
 			$export_helper->OnExportBegin($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnExportCancel(&$event)
 		{
 			$this->OnGoBack($event);
 		}
 
 		/**
 		 * Allows configuring export options
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeExportBegin(&$event)
 		{
 
 		}
 
 		function OnDeleteExportPreset(&$event)
 		{
 			$object =& $event->GetObject();
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info)
 			{
 				list($id,$field_values) = each($items_info);
 				$preset_key = $field_values['ExportPresets'];
 
 				$export_settings = $this->Application->RecallPersistentVar('export_settings');
 				if (!$export_settings) return ;
 				$export_settings = unserialize($export_settings);
 				if (!isset($export_settings[$event->Prefix])) return ;
 
 				$to_delete = '';
 				$export_presets = array(''=>'');
 				foreach ($export_settings[$event->Prefix] as $key => $val) {
 					if (implode('|', $val['ExportColumns']) == $preset_key) {
 						$to_delete = $key;
 						break;
 					}
 				}
 				if ($to_delete) {
 					unset($export_settings[$event->Prefix][$to_delete]);
 					$this->Application->StorePersistentVar('export_settings', serialize($export_settings));
 				}
 			}
 		}
 
 		/**
 		 * Saves changes & changes language
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveAndChangeLanguage(&$event)
 		{
 			$event->CallSubEvent('OnPreSave');
 
 			if ($event->status == erSUCCESS) {
 				$this->Application->SetVar('m_lang', $this->Application->GetVar('language'));
 
 				$pass_vars = Array ('st_id', 'cms_id');
 				foreach ($pass_vars as $pass_var) {
 					$data = $this->Application->GetVar($pass_var);
 					if ($data) {
 						$event->SetRedirectParam($pass_var, $data);
 					}
 				}
 			}
 		}
 
 		/**
 		 * Used to save files uploaded via swfuploader
 		 *
 		 * @param kEvent $event
 		 */
 		function OnUploadFile(&$event)
 		{
 			$event->status = erSTOP;
 			define('DBG_SKIP_REPORTING', 1);
 			echo "Flash requires that we output something or it won't fire the uploadSuccess event";
 
 			if (!$this->Application->HttpQuery->Post) {
 				// Variables {field, id, flashsid} are always submitted through POST!
 				// When file size is larger, then "upload_max_filesize" (in php.ini),
 				// then theese variables also are not submitted -> handle such case.
 				header('HTTP/1.0 413 File size exceeds allowed limit');
 				return ;
 			}
 
 			if (!$this->_checkFlashUploaderPermission($event)) {
 				// 403 Forbidden
 				header('HTTP/1.0 403 You don\'t have permissions to upload');
 				return ;
 			}
 
 			$value = $this->Application->GetVar('Filedata');
 
 			if (!$value || ($value['error'] != UPLOAD_ERR_OK)) {
 				// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
 				// to large for web server to accept, see "upload_max_filesize" in php.ini)
 				header('HTTP/1.0 413 File size exceeds allowed limit');
 				return ;
 			}
 
 			$tmp_path = WRITEABLE . '/tmp/';
 			$fname = $value['name'];
 			$id = $this->Application->GetVar('id');
 			if ($id) {
 				$fname = $id.'_'.$fname;
 			}
 
 			$field_name = $this->Application->GetVar('field');
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 
 			if (array_key_exists($field_name, $fields)) {
 				$upload_dir = $fields[$field_name]['upload_dir'];
 			}
 			else {
 				$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
 				$upload_dir = $virtual_fields[$field_name]['upload_dir'];
 			}
 
 			if (!is_writable($tmp_path) || !is_writable(FULL_PATH . $upload_dir)) {
 				// 500 Internal Server Error
 				// check both temp and live upload directory
 				header('HTTP/1.0 500 Write permissions not set on the server');
 				return ;
 			}
 
 			move_uploaded_file($value['tmp_name'], $tmp_path.$fname);
 		}
 
 		/**
 		 * Checks, that flash uploader is allowed to perform upload
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		function _checkFlashUploaderPermission(&$event)
 		{
 			// Flash uploader does NOT send correct cookies, so we need to make our own check
 			$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
 			$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
 			$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
 
 			// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
 			$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
 
 			$admin_ses =& $this->Application->recallObject('Session.admin');
 			/* @var $admin_ses Session */
 
 			if ($admin_ses->RecallVar('user_id') == -1) {
 				return true;
 			}
 
 			$backup_user_id = $this->Application->RecallVar('user_id'); // 1. backup user
 			$this->Application->StoreVar('user_id', $admin_ses->RecallVar('user_id')); // 2. fake user_id
 
 			$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected'); // 3. event, that have "add|edit" rule
 			$check_event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 
 			$allowed_to_upload = $this->CheckPermission($check_event); // 4. check permission
 
 			$this->Application->StoreVar('user_id', $backup_user_id); // 5. restore user id
 
 			return $allowed_to_upload;
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnDeleteFile(&$event)
 		{
 			$event->status = erSTOP;
 
 			if (strpos($this->Application->GetVar('file'), '../') !== false) {
 				return ;
 			}
 
 			$object =& $event->getObject( Array ('skip_autoload' => true) );
 			$options = $object->GetFieldOptions( $this->Application->GetVar('field') );
 
 			$var_name = $event->getPrefixSpecial() . '_file_pending_actions' . $this->Application->GetVar('m_wid');
 			$schedule = $this->Application->RecallVar($var_name);
 			$schedule = $schedule ? unserialize($schedule) : Array ();
 			$schedule[] = Array ('action' => 'delete', 'file' => $path = FULL_PATH . $options['upload_dir'] . $this->Application->GetVar('file'));
 			$this->Application->StoreVar($var_name, serialize($schedule));
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnViewFile(&$event)
 		{
 			$file = $this->Application->GetVar('file');
 			if ((strpos($file, '../') !== false) || (trim($file) !== $file)) {
 				// when relative paths or special chars are found template names from url, then it's hacking attempt
 				return ;
 			}
 
 			if ($this->Application->GetVar('tmp')) {
 				$path = WRITEABLE . '/tmp/' . $this->Application->GetVar('id') . '_' . $this->Application->GetVar('file');
 			}
 			else {
 				$object =& $event->getObject(array('skip_autoload'=>true));
 				$options = $object->GetFieldOptions($this->Application->GetVar('field'));
 
 				$path = FULL_PATH.$options['upload_dir'].$file;
 			}
 
 			$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
 
 			if (!file_exists($path)) {
 				exit;
 			}
 
 			$type = mime_content_type($path);
 
 			header('Content-Length: '.filesize($path));
 			header('Content-Type: '.$type);
 
 			safeDefine('DBG_SKIP_REPORTING',1);
 
 			readfile($path);
 			exit();
 		}
 
 		/**
 		 * Validates MInput control fields
 		 *
 		 * @param kEvent $event
 		 */
 		function OnValidateMInputFields(&$event)
 		{
 			$minput_helper =& $this->Application->recallObject('MInputHelper');
 			/* @var $minput_helper MInputHelper */
 
 			$minput_helper->OnValidateMInputFields($event);
 		}
 
 		/**
 		 * Returns auto-complete values for ajax-dropdown
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSuggestValues(&$event)
 		{
 			if (!$this->Application->isAdminUser) {
 				// very careful here, because this event allows to
 				// view every object field -> limit only to logged-in admins
 				return ;
 			}
 
 			$event->status = erSTOP;
 
 			$field = $this->Application->GetVar('field');
 			$cur_value = $this->Application->GetVar('cur_value');
 
 			$object =& $event->getObject();
 
 			if (!$field || !$cur_value || !array_key_exists($field, $object->Fields)) {
 				return ;
 			}
 
 			$limit = $this->Application->GetVar('limit');
 			if (!$limit) {
 				$limit = 20;
 			}
 
 			$sql = 'SELECT DISTINCT '.$field.'
 					FROM '.$object->TableName.'
 					WHERE '.$field.' LIKE '.$this->Conn->qstr($cur_value.'%').'
 					ORDER BY '.$field.'
 					LIMIT 0,' . $limit;
 			$data = $this->Conn->GetCol($sql);
 
 			$this->Application->XMLHeader();
 
 			echo '<suggestions>';
 
 			foreach ($data as $item) {
 				echo '<item>' . htmlspecialchars($item) . '</item>';
 			}
 
 			echo '</suggestions>';
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSaveWidths(&$event)
 		{
 			$event->status = erSTOP;
 
 			$lang =& $this->Application->recallObject('lang.current');
 //			header('Content-type: text/xml; charset='.$lang->GetDBField('Charset'));
 
 			$picker_helper =& $this->Application->RecallObject('ColumnPickerHelper');
 			/* @var $picker_helper kColumnPickerHelper */
 			$picker_helper->PreparePicker($event->getPrefixSpecial(), $this->Application->GetVar('grid_name'));
 
 			$picker_helper->SaveWidths($event->getPrefixSpecial(), $this->Application->GetVar('widths'));
 
 			echo 'OK';
 		}
 
 		/**
 		 * Called from CSV import script after item fields
 		 * are set and validated, but before actual item create/update.
 		 * If event status is erSUCCESS, line will be imported,
 		 * else it will not be imported but added to skipped lines
 		 * and displayed in the end of import.
 		 * Event status is preset from import script.
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeCSVLineImport(&$event)
 		{
 			// abstract, for hooking
 		}
 
 		/**
 		 * [HOOK] Allows to add cloned subitem to given prefix
 		 *
 		 * @param kEvent $event
 		 */
 		function OnCloneSubItem(&$event)
 		{
 			$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
 
 			$subitem_prefix = $event->Prefix . '-' . preg_replace('/^#/', '', $event->MasterEvent->Prefix);
 			$clones[$subitem_prefix] = Array ('ParentPrefix' => $event->Prefix);
 			$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
 		}
 	}
\ No newline at end of file
Index: branches/5.0.x/core/kernel/db/dbitem.php
===================================================================
--- branches/5.0.x/core/kernel/db/dbitem.php	(revision 13602)
+++ branches/5.0.x/core/kernel/db/dbitem.php	(revision 13603)
@@ -1,1314 +1,1312 @@
 <?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!');
 
 /**
 * DBItem
 *
 * Desciption
 * @package kernel4
 */
 class kDBItem extends kDBBase {
 
 	/**
 	* Description
 	*
 	* @var array Associative array of current item' field values
 	* @access public
 	*/
 	var $FieldValues;
 
 	/**
 	 * Unformatted field values, before parse
 	 *
 	 * @var Array
 	 * @access private
 	 */
 	var $DirtyFieldValues = Array();
 
 	/**
 	 * Holds item values after loading (not affected by submit)
 	 *
 	 * @var Array
 	 * @access private
 	 */
 	var $OriginalFieldValues = Array ();
 
 	var $FieldErrors;
 
 	var $ErrorMsgs = Array();
 
 	/**
 	* If set to true, Update will skip Validation before running
 	*
 	* @var array Associative array of current item' field values
 	* @access public
 	*/
 	var $IgnoreValidation = false;
 
 	var $Loaded = false;
 
 	/**
 	* Holds item' primary key value
 	*
 	* @var int Value of primary key field for current item
 	* @access public
 	*/
 	var $ID;
 
 	/**
 	 * This object is used in cloning operations
 	 *
 	 * @var bool
 	 */
 	var $inCloning = false;
 
 	function kDBItem()
 	{
 		parent::kDBBase();
 		$this->ErrorMsgs['required'] = '!la_err_required!'; //'Field is required';
 		$this->ErrorMsgs['unique'] = '!la_err_unique!'; //'Field value must be unique';
 		$this->ErrorMsgs['value_out_of_range'] = '!la_err_value_out_of_range!'; //'Field is out of range, possible values from %s to %s';
 		$this->ErrorMsgs['length_out_of_range'] = '!la_err_length_out_of_range!'; //'Field is out of range';
 		$this->ErrorMsgs['bad_type'] = '!la_err_bad_type!'; //'Incorrect data format, please use %s';
 		$this->ErrorMsgs['invalid_format'] = '!la_err_invalid_format!'; //'Incorrect data format, please use %s';
 		$this->ErrorMsgs['bad_date_format'] = '!la_err_bad_date_format!'; //'Incorrect date format, please use (%s) ex. (%s)';
 		$this->ErrorMsgs['primary_lang_required'] = '!la_err_primary_lang_required!';
 	}
 
 	function SetDirtyField($field_name, $field_value)
 	{
 		$this->DirtyFieldValues[$field_name] = $field_value;
 	}
 
 	function GetDirtyField($field_name)
 	{
 		return $this->DirtyFieldValues[$field_name];
 	}
 
 	function GetOriginalField($field_name, $formatted = false, $format=null)
 	{
 		if (array_key_exists($field_name, $this->OriginalFieldValues)) {
 			// item was loaded before
 			$value = $this->OriginalFieldValues[$field_name];
 		}
 		else {
 			// no original fields -> use default field value
 			$value = $this->Fields[$field_name]['default'];
 		}
 
 		if (!$formatted) {
 			return $value;
 		}
 
 		$options = $this->GetFieldOptions($field_name);
 		$res = $value;
 		if (array_key_exists('formatter', $options)) {
 			$formatter =& $this->Application->recallObject($options['formatter']);
 			/* @var $formatter kFormatter */
 
 			$res = $formatter->Format($value, $field_name, $this, $format);
 		}
 		return $res;
 	}
 
 	/**
 	 * Sets original field value (useful for custom virtual fields)
 	 *
 	 * @param string $field_name
 	 */
 	function SetOriginalField($field_name, $field_value)
 	{
 		$this->OriginalFieldValues[$field_name] = $field_value;
 	}
 
 	/**
 	 * Set's default values for all fields
 	 *
 	 * @param bool $populate_ml_fields create all ml fields from db in config or not
 	 *
 	 * @access public
 	 */
 	function SetDefaultValues($populate_ml_fields = false)
 	{
 		parent::SetDefaultValues($populate_ml_fields);
 		if ($populate_ml_fields) {
 			$this->PopulateMultiLangFields();
 		}
 
 		foreach ($this->Fields as $field => $params) {
 			if ( isset($params['default']) ) {
 				$this->SetDBField($field, $params['default']);
 			}
 			else {
 				$this->SetDBField($field, NULL);
 			}
 		}
 	}
 
 	/**
 	* Sets current item field value
 	* (applies formatting)
 	*
 	* @access public
 	* @param string $name Name of the field
 	* @param mixed $value Value to set the field to
 	* @return void
 	*/
 	function SetField($name,$value)
 	{
 		$options = $this->GetFieldOptions($name);
 		$parsed = $value;
 		if ($value == '') {
 			$parsed = NULL;
 		}
 
 		// kFormatter is always used, to make sure, that numeric value is converted to normal representation
 		// according to regional format, even when formatter is not set (try seting format to 1.234,56 to understand why)
 		$formatter =& $this->Application->recallObject(isset($options['formatter']) ? $options['formatter'] : 'kFormatter');
 		$parsed = $formatter->Parse($value, $name, $this);
 
 		$this->SetDBField($name,$parsed);
 	}
 
 	/**
 	* Sets current item field value
 	* (doesn't apply formatting)
 	*
 	* @access public
 	* @param string $name Name of the field
 	* @param mixed $value Value to set the field to
 	* @return void
 	*/
 	function SetDBField($name,$value)
 	{
 		$this->FieldValues[$name] = $value;
 		/*if (isset($this->Fields[$name]['formatter'])) {
 			$formatter =& $this->Application->recallObject($this->Fields[$name]['formatter']);
 			$formatter->UpdateSubFields($name, $value, $this->Fields[$name], $this);
 		}*/
 	}
 
 	/**
 	 * Set's field error, if pseudo passed not found then create it with message text supplied.
 	 * Don't owerrite existing pseudo translation.
 	 *
 	 * @param string $field
 	 * @param string $pseudo
 	 * @param string $error_label
 	 *
 	 * @return bool
 	 */
 	function SetError($field, $pseudo, $error_label = null, $error_params = null)
 	{
 		$error_field = isset($this->Fields[$field]['error_field']) ? $this->Fields[$field]['error_field'] : $field;
 		if (isset($this->FieldErrors[$error_field]['pseudo'])) {
 			// don't set more then one error on field
 			return false;
 		}
 
 		$this->FieldErrors[$error_field]['pseudo'] = $pseudo;
 
 		if (isset($error_params)) {
 			if (array_key_exists('value', $error_params)) {
 				$this->FieldErrors[$error_field]['value'] = $error_params['value'];
 				unset($error_params['value']);
 			}
 
 			// additional params, that helps to determine error sources
 			$this->FieldErrors[$error_field]['params'] = $error_params;
 		}
 
 		if (isset($error_label) && !isset($this->ErrorMsgs[$pseudo])) {
 			// label for error (only when not already set)
 			$this->ErrorMsgs[$pseudo] = (substr($error_label, 0, 1) == '+') ? substr($error_label, 1) : '!'.$error_label.'!';
 		}
 
 		return true;
 	}
 
 	/**
 	* Return current item' field value by field name
 	* (doesn't apply formatter)
 	*
 	* @access public
 	* @param string $name field name to return
 	* @return mixed
 	*/
 	function GetDBField($name)
 	{
 		/*if (!array_key_exists($name, $this->FieldValues) && defined('DEBUG_MODE') && DEBUG_MODE) {
 			$this->Application->Debugger->appendTrace();
 		}*/
 
 		return $this->FieldValues[$name];
 	}
 
 	function HasField($name)
 	{
 		return isset($this->FieldValues[$name]);
 	}
 
 	function GetFieldValues()
 	{
 		return $this->FieldValues;
 	}
 
 	/**
 	* Sets item' fields corresponding to elements in passed $hash values.
 	*
 	* The function sets current item fields to values passed in $hash, by matching $hash keys with field names
 	* of current item. If current item' fields are unknown {@link kDBItem::PrepareFields()} is called before acutally setting the fields
 	*
 	* @access public
 	* @param Array $hash
 	* @param Array $set_fields Optional param, field names in target object to set, other fields will be skipped
 	* @return void
 	*/
 	function SetFieldsFromHash($hash, $set_fields = null)
 	{
 		// used in formatter which work with multiple fields together
 		foreach($hash as $field_name => $field_value) {
 			if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
 				continue;
 			}
 
 			if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
 				continue;
 			}
 
 			$this->SetDirtyField($field_name, $field_value);
 		}
 
 		// formats all fields using associated formatters
 		foreach ($hash as $field_name => $field_value)
 		{
 			if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
 				continue;
 			}
 
 			if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
 				continue;
 			}
 
 			$this->SetField($field_name,$field_value);
 		}
 	}
 
 	function SetDBFieldsFromHash($hash, $set_fields = null)
 	{
 		foreach ($hash as $field_name => $field_value) {
 			if (is_numeric($field_name) || !array_key_exists($field_name, $this->Fields)) {
 				continue;
 			}
 
 			if (is_array($set_fields) && !in_array($field_name, $set_fields)) {
 				continue;
 			}
 
 			$this->SetDBField($field_name, $field_value);
 		}
 	}
 
 	/**
 	* Returns part of SQL WHERE clause identifing the record, ex. id = 25
 	*
 	* @access public
 	* @param string $method Child class may want to know who called GetKeyClause, Load(), Update(), Delete() send its names as method
 	* @param Array $keys_hash alternative, then item id, keys hash to load item by
 	* @return void
 	* @see kDBItem::Load()
 	* @see kDBItem::Update()
 	* @see kDBItem::Delete()
 	*/
 	function GetKeyClause($method=null, $keys_hash = null)
 	{
 		if (!isset($keys_hash)) {
 			$keys_hash = Array ($this->IDField => $this->ID);
 		}
 
 		$ret = '';
 
 		foreach ($keys_hash as $field => $value) {
 			if (!preg_match('/\./', $field)) {
 				$ret .= '(`' . $this->TableName . '`.' . $field . ' = ' . $this->Conn->qstr($value) . ') AND ';
 			}
 			else {
 				$ret .= '(' . $field . ' = ' . $this->Conn->qstr($value) . ') AND ';
 			}
 		}
 
 		return substr($ret, 0, -5);
 	}
 
 	/**
 	* Loads item from the database by given id
 	*
 	* @access public
 	* @param mixed $id item id of keys->values hash to load item by
 	* @param string $id_field_name Optional parameter to load item by given Id field
 	* @return bool True if item has been loaded, false otherwise
 	*/
 	function Load($id, $id_field_name = null)
 	{
 		if ( isset($id_field_name) ) {
 			$this->SetIDField($id_field_name); // set new IDField
 		}
 
 		$keys_sql = '';
 		if (is_array($id)) {
 			$keys_sql = $this->GetKeyClause('load', $id);
 		}
 		else {
 			$this->setID($id);
 			$keys_sql = $this->GetKeyClause('load');
 		}
 
 		if ( isset($id_field_name) ) {
 			// restore original IDField from unit config
 			$this->setIDField( $this->Application->getUnitOption($this->Prefix, 'IDField') );
 		}
 
 		if (($id === false) || !$keys_sql) {
 			return $this->Clear();
 		}
 
 		if (!$this->raiseEvent('OnBeforeItemLoad', $id)) {
 			return false;
 		}
 
 		$q = $this->GetSelectSQL() . ' WHERE ' . $keys_sql;
 		$field_values = $this->Conn->GetRow($q);
 
 		if ($field_values) {
 			$this->FieldValues = array_merge_recursive2($this->FieldValues, $field_values);
 			$this->OriginalFieldValues = $this->FieldValues;
 		}
 		else {
 			return $this->Clear();
 		}
 
 		if (is_array($id) || isset($id_field_name)) {
 			$this->setID($this->FieldValues[$this->IDField]);
 		}
 
 		$this->UpdateFormattersSubFields(); // used for updating separate virtual date/time fields from DB timestamp (for example)
 
 		$this->raiseEvent('OnAfterItemLoad', $this->GetID());
 		$this->Loaded = true;
 
 		return true;
 	}
 
 	/**
 	* Builds select sql, SELECT ... FROM parts only
 	*
 	* @access public
 	* @return string
 	*/
 	function GetSelectSQL()
 	{
 		$sql = $this->addCalculatedFields($this->SelectClause);
 		return parent::GetSelectSQL($sql);
 	}
 
 	function UpdateFormattersMasterFields()
 	{
 		foreach ($this->Fields as $field => $options) {
 			if (isset($options['formatter'])) {
 				$formatter =& $this->Application->recallObject($options['formatter']);
 				$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
 			}
 		}
 	}
 
 	/**
 	 * Allows to skip certain fields from getting into sql queries
 	 *
 	 * @param string $field_name
 	 * @param mixed $force_id
 	 * @return bool
 	 */
 	function skipField($field_name, $force_id = false)
 	{
 		$skip = false;
 
 		// 1. skipping 'virtual' field
 		$skip = $skip || array_key_exists($field_name, $this->VirtualFields);
 
 		// 2. don't write empty field value to db, when "skip_empty" option is set
 		$field_value = array_key_exists($field_name, $this->FieldValues) ? $this->FieldValues[$field_name] : false;
 
 		if (array_key_exists($field_name, $this->Fields)) {
 			$skip_empty = array_key_exists('skip_empty', $this->Fields[$field_name]) ? $this->Fields[$field_name]['skip_empty'] : false;
 		}
 		else {
 			// field found in database, but not declared in unit config
 			$skip_empty = false;
 		}
 
 		$skip = $skip || (!$field_value && $skip_empty);
 
 		// 3. skipping field not in Fields (nor virtual, nor real)
 		$skip = $skip || !array_key_exists($field_name, $this->Fields);
 
 		return $skip;
 	}
 
 	/**
 	* Updates previously loaded record with current item' values
 	*
 	* @access public
 	* @param int Primery Key Id to update
 	* @return bool
 	*/
 	function Update($id = null, $system_update = false)
 	{
 		if (isset($id)) {
 			$this->setID($id);
 		}
 
 		if (!$this->raiseEvent('OnBeforeItemUpdate')) {
 			return false;
 		}
 
 		if (!isset($this->ID)) {
 			// ID could be set inside OnBeforeItemUpdate event, so don't combine this check with previous one
 			return false;
 		}
 
 		// validate before updating
 		if (!$this->Validate()) {
 			return false;
 		}
 
 	    if (!$this->FieldValues) {
 	    	// nothing to update
 	    	return true;
 	    }
 
 	    $sql = '';
 
 	    foreach ($this->FieldValues as $field_name => $field_value) {
 	    	if ($this->skipField($field_name)) {
 	    		continue;
 	    	}
 
 	    	if ( is_null($field_value) ) {
 	    		if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
 	    			// "kFormatter::Parse" methods converts empty values to NULL and for
 	    			// not-null fields they are replaced with default value here
 	    			$field_value = $this->Fields[$field_name]['default'];
 	    		}
 	    	}
 
 	    	$sql .= '`' . $field_name . '` = ' . $this->Conn->qstr($field_value) . ', ';
 	   	}
 
 	   	$sql = 'UPDATE ' . $this->TableName . '
 	   			SET ' . substr($sql, 0, -2) . '
 	   			WHERE ' . $this->GetKeyClause('update');
 
 	    if ($this->Conn->ChangeQuery($sql) === false) {
 	    	// there was and sql error
 	    	return false;
 	    }
 
 		$affected_rows = $this->Conn->getAffectedRows();
 
 		if (!$system_update && ($affected_rows > 0)) {
 			$this->setModifiedFlag(clUPDATE);
 		}
 
 		$this->saveCustomFields();
 
 		if ($affected_rows > 0) {
 	    	$this->raiseEvent('OnAfterItemUpdate');
 		}
 
 	    $this->OriginalFieldValues = $this->FieldValues;
 	    $this->Loaded = true;
 
 	    if ($this->mode != 't') {
 			$this->Application->resetCounters($this->TableName);
 		}
 
 		return true;
 	}
 
 	function ValidateField($field)
 	{
 		$options = $this->Fields[$field];
 
 		/*if (isset($options['formatter'])) {
 			$formatter =& $this->Application->recallObject($options['formatter']);
 			$formatter->UpdateMasterFields($field, $this->GetDBField($field), $options, $this);
 		}*/
 
 		$error_field = isset($options['error_field']) ? $options['error_field'] : $field;
 		$res = !isset($this->FieldErrors[$error_field]['pseudo']) || !$this->FieldErrors[$error_field]['pseudo'];
 
 		$res = $res && $this->ValidateRequired($field, $options);
 		$res = $res && $this->ValidateType($field, $options);
 		$res = $res && $this->ValidateRange($field, $options);
 		$res = $res && $this->ValidateUnique($field, $options);
 		$res = $res && $this->CustomValidation($field, $options);
 
 		return $res;
 	}
 
 	/**
 	 * Validate all item fields based on
 	 * constraints set in each field options
 	 * in config
 	 *
 	 * @return bool
 	 * @access private
 	 */
 	function Validate()
 	{
 		$this->UpdateFormattersMasterFields(); //order is critical - should be called BEFORE checking errors
 
 		if ($this->IgnoreValidation) {
 			return true;
 		}
 
 		// will apply any custom validation to the item
 		$this->raiseEvent('OnBeforeItemValidate');
 
 		$global_res = true;
 		foreach ($this->Fields as $field => $params) {
 			$res = $this->ValidateField($field);
 
 
 			$global_res = $global_res && $res;
 		}
 
 		if (!$global_res && $this->Application->isDebugMode()) {
 			$error_msg = '	Validation failed in prefix <strong>'.$this->Prefix.'</strong>,
 							FieldErrors follow (look at items with <strong>"pseudo"</strong> key set)<br />
 							You may ignore this notice if submitted data really has a validation error';
 			trigger_error(trim($error_msg), E_USER_NOTICE);
 			$this->Application->Debugger->dumpVars($this->FieldErrors);
 		}
 
 		if ($global_res) {
 			// no validation errors
 			$this->raiseEvent('OnAfterItemValidate');
 		}
 
 		return $global_res;
 	}
 
 	/**
 	 * Check field value by user-defined alghoritm
 	 *
 	 * @param string $field field name
 	 * @param Array $params field options from config
 	 * @return bool
 	 */
 	function CustomValidation($field, $params)
 	{
 		return true;
 	}
 
 	/**
 	 * Check if item has errors
 	 *
 	 * @param Array $skip_fields fields to skip during error checking
 	 * @return bool
 	 */
 	function HasErrors($skip_fields)
 	{
 		$global_res = false;
 
 		foreach ($this->Fields as $field => $field_params) {
 			// If Formatter has set some error messages during values parsing
 			if ( !( in_array($field, $skip_fields) ) &&
 				isset($this->FieldErrors[$field]['pseudo']) && $this->FieldErrors[$field] != '') {
 				$global_res = true;
 			}
 		}
 		return $global_res;
 	}
 
 	/**
 	 * Check if value in field matches field type specified in config
 	 *
 	 * @param string $field field name
 	 * @param Array $params field options from config
 	 * @return bool
 	 */
 	function ValidateType($field, $params)
 	{
 		$res = true;
 		$val = $this->FieldValues[$field];
 		if ( 	$val != '' &&
 					isset($params['type']) &&
 					preg_match("#int|integer|double|float|real|numeric|string#", $params['type'])
 				) {
 			if ($params['type'] == 'numeric') {
 				trigger_error('Invalid field type <strong>'.$params['type'].'</strong> (in ValidateType method), please use <strong>float</strong> instead', E_USER_NOTICE);
 				$params['type'] = 'float';
 			}
 			$res = is_numeric($val);
 			if ($params['type']=='string' || $res) {
 				$f = 'is_'.$params['type'];
 				settype($val, $params['type']);
 				$res = $f($val) && ($val == $this->FieldValues[$field]);
 			}
 			if (!$res) {
 				$this->SetError($field, 'bad_type', null, $params['type']);
 			}
 		}
 		return $res;
 	}
 
 	/**
 	 * Check if value is set for required field
 	 *
 	 * @param string $field field name
 	 * @param Array $params field options from config
 	 * @return bool
 	 * @access private
 	 */
 	function ValidateRequired($field, $params)
 	{
 		$res = true;
 		if (isset($params['required']) && $params['required']) {
 			$check_value = $this->FieldValues[$field];
 			if ($this->Application->ConfigValue('TrimRequiredFields')) {
 				$check_value = trim($check_value);
 			}
 			$res = ((string)$check_value != '');
 		}
 
 		if (!$res) {
 			$this->SetError($field, 'required');
 		}
 
 		return $res;
 	}
 
 	/**
 	 * Validates that current record has unique field combination among other table records
 	 *
 	 * @param string $field field name
 	 * @param Array $params field options from config
 	 * @return bool
 	 * @access private
 	 */
 	function ValidateUnique($field, $params)
 	{
 		$res = true;
 		$unique_fields = getArrayValue($params,'unique');
 		if($unique_fields !== false)
 		{
 			$where = Array();
 			array_push($unique_fields,$field);
 			foreach($unique_fields as $unique_field)
 			{
 				// if field is not empty or if it is required - we add where condition
 				if ((string)$this->GetDBField($unique_field) != '' || (isset($this->Fields[$unique_field]['required']) && $this->Fields[$unique_field]['required'])) {
 					$where[] = '`'.$unique_field.'` = '.$this->Conn->qstr( $this->GetDBField($unique_field) );
 				}
 				else {
 					// not good if we check by less fields than indicated
 					return true;
 				}
 			}
 			// This can ONLY happen if all unique fields are empty and not required.
 			// In such case we return true, because if unique field is not required there may be numerous empty values
 //			if (!$where) return true;
 
 			$sql = 'SELECT COUNT(*) FROM %s WHERE ('.implode(') AND (',$where).') AND ('.$this->IDField.' <> '.(int)$this->ID.')';
 
 			$res_temp = $this->Conn->GetOne( str_replace('%s', $this->TableName, $sql) );
 
 			$current_table_only = getArrayValue($params, 'current_table_only'); // check unique record only in current table
 			$res_live = $current_table_only ? 0 : $this->Conn->GetOne( str_replace('%s', $this->Application->GetLiveName($this->TableName), $sql) );
 
 			$res = ($res_temp == 0) && ($res_live == 0);
 
 			if (!$res) {
 				$this->SetError($field, 'unique');
 			}
 		}
 		return $res;
 	}
 
 	/**
 	 * Check if field value is in range specified in config
 	 *
 	 * @param string $field field name
 	 * @param Array $params field options from config
 	 * @return bool
 	 * @access private
 	 */
 	function ValidateRange($field, $params)
 	{
 		$res = true;
 		$val = $this->FieldValues[$field];
 
 		if ( isset($params['type']) && preg_match("#int|integer|double|float|real#", $params['type']) && strlen($val) > 0 ) {
 			if ( isset($params['max_value_inc'])) {
 				$res = $res && $val <= $params['max_value_inc'];
 				$max_val = $params['max_value_inc'].' (inclusive)';
 			}
 			if ( isset($params['min_value_inc'])) {
 				$res = $res && $val >= $params['min_value_inc'];
 				$min_val = $params['min_value_inc'].' (inclusive)';
 			}
 			if ( isset($params['max_value_exc'])) {
 				$res = $res && $val < $params['max_value_exc'];
 				$max_val = $params['max_value_exc'].' (exclusive)';
 			}
 			if ( isset($params['min_value_exc'])) {
 				$res = $res && $val > $params['min_value_exc'];
 				$min_val = $params['min_value_exc'].' (exclusive)';
 			}
 		}
 		if (!$res) {
 			if ( !isset($min_val) ) $min_val = '-&infin;';
 			if ( !isset($max_val) ) $max_val = '&infin;';
 
 			$this->SetError($field, 'value_out_of_range', null, Array ($min_val, $max_val));
 			return $res;
 		}
 		if ( isset($params['max_len'])) {
 			$res = $res && mb_strlen($val) <= $params['max_len'];
 		}
 		if ( isset($params['min_len'])) {
 			$res = $res && mb_strlen($val) >= $params['min_len'];
 		}
 		if (!$res) {
 			$error_params = Array (getArrayValue($params, 'min_len'), getArrayValue($params, 'max_len'));
 			$this->SetError($field, 'length_out_of_range', null, $error_params);
 			return $res;
 		}
 		return $res;
 	}
 
 	/**
 	 * Return error message for field
 	 *
 	 * @param string $field
 	 * @return string
 	 * @access public
 	 */
 	function GetErrorMsg($field, $force_escape = null)
 	{
 		if( !isset($this->FieldErrors[$field]) ) return '';
 
 		$err = getArrayValue($this->FieldErrors[$field], 'pseudo');
 		if (!$err) return '';
 		// if special error msg defined in config
 		if( isset($this->Fields[$field]['error_msgs'][$err]) )
 		{
 			$msg = $this->Fields[$field]['error_msgs'][$err];
 		}
 		else //fall back to defaults
 		{
 			if( !isset($this->ErrorMsgs[$err]) ) {
 				trigger_error('No user message is defined for pseudo error <b>'.$err.'</b><br>', E_USER_WARNING);
 				return $err; //return the pseudo itself
 			}
 			$msg = $this->ErrorMsgs[$err];
 		}
 		$msg = $this->Application->ReplaceLanguageTags($msg, $force_escape);
 
 		if ( isset($this->FieldErrors[$field]['params']) )
 		{
 			return vsprintf($msg, $this->FieldErrors[$field]['params']);
 		}
 		return $msg;
 	}
 
 	/**
 	* Creates a record in the database table with current item' values
 	*
 	* @param mixed $force_id Set to TRUE to force creating of item's own ID or to value to force creating of passed id. Do not pass 1 for true, pass exactly TRUE!
 	* @access public
 	* @return bool
 	*/
 	function Create($force_id = false, $system_create = false)
 	{
 		if (!$this->raiseEvent('OnBeforeItemCreate')) {
 			return false;
 		}
 
 		// Validating fields before attempting to create record
 		if (!$this->Validate()) {
 			return false;
 		}
 
 		if (is_int($force_id)) {
 			$this->FieldValues[$this->IDField] = $force_id;
 		}
 		elseif (!$force_id || !is_bool($force_id)) {
 			$this->FieldValues[$this->IDField] = $this->generateID();
 		}
 
 		$fields_sql = '';
 		$values_sql = '';
 		foreach ($this->FieldValues as $field_name => $field_value) {
 			if ($this->skipField($field_name, $force_id)) {
 				continue;
 			}
 
 			if (is_null($field_value)) {
 				if (array_key_exists('not_null', $this->Fields[$field_name]) && $this->Fields[$field_name]['not_null']) {
 					// "kFormatter::Parse" methods converts empty values to NULL and for
 	    			// not-null fields they are replaced with default value here
 					$values_sql .= $this->Conn->qstr($this->Fields[$field_name]['default']);
 				}
 				else {
 					$values_sql .= $this->Conn->qstr($field_value);
 				}
 			}
 			else {
 				if (($field_name == $this->IDField) && ($field_value == 0)) {
 					// don't skip IDField in INSERT statement, just use DEFAULT keyword as it's value
 					$values_sql .= 'DEFAULT';
 				}
 				else {
 					$values_sql .= $this->Conn->qstr($field_value);
 				}
 			}
 
 			$fields_sql .= '`' . $field_name . '`, '; //Adding field name to fields block of Insert statement
 			$values_sql .= ', ';
 		}
 
 		$sql = 'INSERT INTO ' . $this->TableName . ' (' . substr($fields_sql, 0, -2) . ')
 				VALUES (' . substr($values_sql, 0, -2) . ')';
 
 		//Executing the query and checking the result
 		if ($this->Conn->ChangeQuery($sql) === false) {
 			return false;
 		}
 
 		$this->OriginalFieldValues = $this->FieldValues;
 
 		$insert_id = $this->Conn->getInsertID();
 		if ($insert_id == 0) {
 			// insert into temp table (id is not auto-increment field)
 			$insert_id = $this->FieldValues[$this->IDField];
 		}
 		$this->setID($insert_id);
 
 		if (!$system_create){
 			$this->setModifiedFlag(clCREATE);
 		}
 
 		$this->saveCustomFields();
 		if ($this->mode != 't') {
 			$this->Application->resetCounters($this->TableName);
 		}
+
+		if ($this->IsTempTable() && ($this->Application->GetTopmostPrefix($this->Prefix) != $this->Prefix)) {
+			// temp table + subitem = set negative id
+			$this->setTempID();
+		}
+
 		$this->raiseEvent('OnAfterItemCreate');
 		$this->Loaded = true;
 
 		return true;
 	}
 
 	/**
 	* Deletes the record from databse
 	*
 	* @access public
 	* @return bool
 	*/
 	function Delete($id = null)
 	{
 		if (isset($id)) {
 			$this->setID($id);
 		}
 
 		if (!$this->raiseEvent('OnBeforeItemDelete')) {
 			return false;
 		}
 
 		$sql = 'DELETE FROM ' . $this->TableName . '
 				WHERE ' . $this->GetKeyClause('Delete');
 
 		$ret = $this->Conn->ChangeQuery($sql);
 		$affected_rows = $this->Conn->getAffectedRows();
 
 		$this->setModifiedFlag(clDELETE); // will change affected rows, so get it before this line
 
 		if ($affected_rows > 0) {
 			// something was actually deleted
 			$this->raiseEvent('OnAfterItemDelete');
 		}
 
 		if ($this->mode != 't') {
 			$this->Application->resetCounters($this->TableName);
 		}
 
 		return $ret;
 	}
 
 	function PopulateMultiLangFields()
 	{
 		foreach ($this->Fields as $field => $options) {
 			// master field is set only for CURRENT language
 			$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
 
 			if (($formatter == 'kMultiLanguage') && array_key_exists('master_field', $options) && array_key_exists('error_field', $options)) {
 				// MuliLanguage formatter sets error_field to master_field, but in PopulateMlFields mode,
 				// we display ML fields directly so we set it back to itself, otherwise error will not be displayed
 				unset($this->Fields[$field]['error_field']);
 			}
 		}
 	}
 
 	/**
 	 * Sets new name for item in case if it is beeing copied
 	 * in same table
 	 *
 	 * @param array $master Table data from TempHandler
 	 * @param int $foreign_key ForeignKey value to filter name check query by
 	 * @param string $title_field FieldName to alter, by default - TitleField of the prefix
 	 * @param string $format sprintf-style format of renaming pattern, by default Copy %1$s of %2$s which makes it Copy [Number] of Original Name
 	 * @access private
 	 */
 	function NameCopy($master=null, $foreign_key=null, $title_field=null, $format='Copy %1$s of %2$s')
 	{
 		if (!isset($title_field)) {
 		$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
 		if (!$title_field || isset($this->CalculatedFields[$title_field]) ) return;
 		}
 
 		$new_name = $this->GetDBField($title_field);
 		$original_checked = false;
 		do {
 			if ( preg_match('/'.sprintf($format, '([0-9]*) *', '(.*)').'/', $new_name, $regs) ) {
 				$new_name = sprintf($format, ($regs[1]+1), $regs[2]);
 			}
 			elseif ($original_checked) {
 				$new_name = sprintf($format, '', $new_name);
 			}
 
 			// if we are cloning in temp table this will look for names in temp table,
 			// since object' TableName contains correct TableName (for temp also!)
 			// if we are cloning live - look in live
 			$query = 'SELECT '.$title_field.' FROM '.$this->TableName.'
 								WHERE '.$title_field.' = '.$this->Conn->qstr($new_name);
 
 			$foreign_key_field = getArrayValue($master, 'ForeignKey');
 			$foreign_key_field = is_array($foreign_key_field) ? $foreign_key_field[ $master['ParentPrefix'] ] : $foreign_key_field;
 
 			if ($foreign_key_field && isset($foreign_key)) {
 				$query .= ' AND '.$foreign_key_field.' = '.$foreign_key;
 			}
 
 			$res = $this->Conn->GetOne($query);
 
 			/*// if not found in live table, check in temp table if applicable
 			if ($res === false && $object->Special == 'temp') {
 				$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
 				$res = $this->Conn->GetOne($query);
 			}*/
 
 			$original_checked = true;
 		} while ($res !== false);
 		$this->SetDBField($title_field, $new_name);
 	}
 
 	function raiseEvent($name, $id = null, $additional_params = Array())
 	{
 		if( !isset($id) ) $id = $this->GetID();
 		$event = new kEvent( Array('name'=>$name,'prefix'=>$this->Prefix,'special'=>$this->Special) );
 		$event->setEventParam('id', $id);
 
 		if ($additional_params) {
 			foreach ($additional_params as $ap_name => $ap_value) {
 				$event->setEventParam($ap_name, $ap_value);
 			}
 		}
 
 		$this->Application->HandleEvent($event);
 		return $event->status == erSUCCESS ? true : false;
 	}
 
 	/**
 	 * Set's new ID for item
 	 *
 	 * @param int $new_id
 	 * @access public
 	 */
 	function setID($new_id)
 	{
 		$this->ID = $new_id;
 		$this->SetDBField($this->IDField, $new_id);
 	}
 
 	/**
 	 * Generate and set new temporary id
 	 *
 	 * @access private
 	 */
 	function setTempID()
 	{
 		$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$this->IDField.') FROM '.$this->TableName);
 		if($new_id > 0) $new_id = 0;
 		--$new_id;
 
 		$this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID());
 
 		if ($this->ShouldLogChanges()) {
 			// Updating TempId in ChangesLog, if changes are disabled
 			$ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix).'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
 			$changes = $this->Application->RecallVar($ses_var_name);
 			$changes = $changes ? unserialize($changes) : Array ();
 			if ($changes) {
 				foreach ($changes as $key => $rec) {
 					if ($rec['Prefix'] == $this->Prefix && $rec['ItemId'] == $this->GetID()) {
 						$changes[$key]['ItemId'] = $new_id;
 					}
 				}
 			}
 			$this->Application->StoreVar($ses_var_name, serialize($changes));
 		}
 
 		$this->SetID($new_id);
 	}
 
 	/**
 	 * Set's modification flag for main prefix of current prefix to true
 	 *
 	 * @access private
 	 * @author Alexey
 	 */
 	function setModifiedFlag($mode = null)
 	{
 		$main_prefix = $this->Application->GetTopmostPrefix($this->Prefix);
 		$this->Application->StoreVar($main_prefix.'_modified', '1', !$this->Application->isAdmin);
 
 		if ($this->ShouldLogChanges()) {
 			$this->LogChanges($main_prefix, $mode);
 			if (!$this->IsTempTable()) {
 				$handler =& $this->Application->recallObject($this->Prefix.'_EventHandler');
 				$ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
 				$handler->SaveLoggedChanges($ses_var_name);
 			}
 		}
 	}
 
 	/**
 	 * Determines, that changes made to this item should be written to change log
 	 *
 	 * @return bool
 	 */
 	function ShouldLogChanges()
 	{
 		$log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog');
 
 		return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges');
 	}
 
 	function LogChanges($main_prefix, $mode)
 	{
 		if (!$mode) {
 			return ;
 		}
 
 		$ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix);
 		$changes = $this->Application->RecallVar($ses_var_name);
 		$changes = $changes ? unserialize($changes) : array();
 
 		$general = array(
 			'Prefix' => $this->Prefix,
 			'ItemId' => $this->GetID(),
 			'OccuredOn' => adodb_mktime(),
 			'MasterPrefix' => $main_prefix,
 			'MasterId' => $this->Prefix == $main_prefix ? $this->GetID() : $this->Application->GetVar($main_prefix.'_id'), // is that correct (Kostja)??
 			'Action' => $mode,
 		);
 		switch ($mode) {
 			case clUPDATE:
 				$changes[] = array_merge($general, Array(
 					'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetChangedFields())),
 				));
 				break;
 			case clCREATE:
 				$changes[] = array_merge($general, Array(
 					'Changes' => serialize($this->GetTitleField()),
 				));
 				break;
 			case clDELETE:
 				$changes[] = array_merge($general, Array(
 					'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetRealFields())),
 				));
 		}
 
 		$this->Application->StoreVar($ses_var_name, serialize($changes));
 	}
 
 	function GetTitleField()
 	{
 		$title_field = $this->Application->getUnitOption($this->Prefix, 'TitleField');
 		if ($title_field && $this->GetField($title_field)) {
 			return Array($title_field => $this->GetField($title_field));
 		}
 	}
 
 	function GetRealFields()
 	{
 		if (function_exists('array_diff_key')) {
 			$db_fields = array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields);
 		}
 		else {
 			$db_fields = array();
 			foreach ($this->FieldValues as $key => $value) {
 				if (array_key_exists($key, $this->VirtualFields) || array_key_exists($key, $this->CalculatedFields)) continue;
 				$db_fields[$key] = $value;
 			}
 		}
 		return $db_fields;
 	}
 
 	function GetChangedFields()
 	{
 		$changes = array();
 
 		$diff = array_diff_assoc($this->GetRealFields(), $this->OriginalFieldValues);
 		foreach ($diff as $field => $new_value) {
 			$changes[$field] = array('old' => $this->GetOriginalField($field, true), 'new' => $this->GetField($field));
 		}
 		return $changes;
 	}
 
 	/**
 	 * Returns ID of currently processed record
 	 *
 	 * @return int
 	 * @access public
 	 */
 	function GetID()
 	{
 		return $this->ID;
 	}
 
 	/**
 	 * Generates ID for new items before inserting into database
 	 *
 	 * @return int
 	 * @access private
 	 */
 	function generateID()
 	{
 		return 0;
 	}
 
 	/**
 	 * Returns true if item was loaded successfully by Load method
 	 *
 	 * @return bool
 	 */
 	function isLoaded()
 	{
 		return $this->Loaded;
 	}
 
 	/**
 	 * Checks if field is required
 	 *
 	 * @param string $field
 	 * @return bool
 	 */
 	function isRequired($field)
 	{
 		return getArrayValue( $this->Fields[$field], 'required' );
 	}
 
 	/**
 	 * Sets new required flag to field
 	 *
 	 * @param string $field
 	 * @param bool $is_required
 	 */
 	function setRequired($field, $is_required = true)
 	{
 		$this->Fields[$field]['required'] = $is_required;
 	}
 
 	function Clear($new_id = null)
 	{
 		$this->Loaded = false;
 		$this->FieldValues = Array();
 		$this->OriginalFieldValues = Array ();
 		$this->SetDefaultValues(); // will wear off kDBItem::setID effect, so set it later
 		$this->FieldErrors = Array();
 
 		$this->setID($new_id);
 
 		return $this->Loaded;
 	}
 
 	function Query($force = false)
 	{
 		if ($this->Application->isDebugMode()) {
 			$this->Application->Debugger->appendTrace();
 		}
 
 		trigger_error('<b>Query</b> method is called in class <b>'.get_class($this).'</b> for prefix <b>'.$this->getPrefixSpecial().'</b>', E_USER_ERROR);
 	}
 
 	function saveCustomFields()
 	{
 		if (!$this->customFields || $this->inCloning) {
 			return true;
 		}
 
 		$cdata_key = rtrim($this->Prefix.'-cdata.'.$this->Special, '.');
 		$cdata =& $this->Application->recallObject($cdata_key, null, Array('skip_autoload' => true, 'populate_ml_fields' => true));
 
 		$resource_id = $this->GetDBField('ResourceId');
 		$cdata->Load($resource_id, 'ResourceId');
 		$cdata->SetDBField('ResourceId', $resource_id);
 
 		$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
 		/* @var $ml_formatter kMultiLanguage */
 
 		foreach ($this->customFields as $custom_id => $custom_name) {
 			$force_primary = isset($cdata->Fields['cust_'.$custom_id]['force_primary']) && $cdata->Fields['cust_'.$custom_id]['force_primary'];
 			$cdata->SetDBField($ml_formatter->LangFieldName('cust_'.$custom_id, $force_primary), $this->GetDBField('cust_'.$custom_name));
 		}
 
-		if ($cdata->isLoaded()) {
-			$ret = $cdata->Update();
-		}
-		else {
-			 $ret = $cdata->Create();
-			 if ($cdata->mode == 't') $cdata->setTempID();
-		}
-
-		return $ret;
+		return $cdata->isLoaded() ? $cdata->Update() : $cdata->Create();
 	}
 
 	/**
 	 * Returns specified field value from all selected rows.
 	 * Don't affect current record index
 	 *
 	 * @param string $field
 	 * @return Array
 	 */
 	function GetCol($field)
 	{
 		return Array (0 => $this->GetDBField($field));
 	}
 
 }
\ No newline at end of file
Index: branches/5.0.x/core/units/helpers/controls/edit_picker_helper.php
===================================================================
--- branches/5.0.x/core/units/helpers/controls/edit_picker_helper.php	(revision 13602)
+++ branches/5.0.x/core/units/helpers/controls/edit_picker_helper.php	(revision 13603)
@@ -1,181 +1,177 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class EditPickerHelper extends kHelper {
 
 
 		function getTable($prefix, $temp=false)
 		{
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			return $temp ? $this->Application->GetTempName($table_name, 'prefix:'.$prefix) : $table_name;
 		}
 
 		/**
 		 * Applies filter for multiple lists in inp_edit_picker control.
 		 * Called from SetCustomQuery of prefix, that contains all available items.
 		 *
 		 * @param kEvent $event
 		 * @param string $storage_field main item's field name, where values are located
 		 */
 		function applyFilter(&$event, $storage_field)
 		{
 			if ($event->Special != 'selected' && $event->Special != 'available') {
 				return ;
 			}
 
 			if ($storage_field != $event->getEventParam('link_to_field')) {
 				return ;
 			}
 
 			$object =& $event->getObject();
 			/* @var $object kDBList */
 
 			$main_object =& $this->Application->recallObject($event->getEventParam('link_to_prefix'));
 			/* @var $main_object kDBItem */
 
 			$selected_items = $main_object->GetDBField($storage_field);
 			if ($selected_items) {
 				$filter_type = $event->Special == 'selected' ? 'IN' : 'NOT IN';
 				$selected_items = explode('|', substr($selected_items, 1, -1));
 				$filter_clause = '%1$s.' . $object->IDField.' '.$filter_type.' ('.implode(',', $selected_items).')';
 			}
 			else {
 				$filter_clause = ($event->Special == 'selected') ? 'FALSE' : 'TRUE';
 			}
 
 			$constrain = $this->_getConstrain($main_object, $storage_field, 'filter');
 			if ($constrain) {
 				$filter_clause .= ' AND (' . $constrain . ')';
 			}
 
 			$object->addFilter('edit_picker_filter', $filter_clause);
 		}
 
 		/**
 		 * Loads selected values from sub_prefix to main item virtual field.
 		 * Called from OnAfterItemLoad of main prefix.
 		 *
 		 * @param kEvent $event
 		 * @param string $store_field main item's field name, to store values into
 		 * @param string $source_field prefix and it's field used to store info about selected items (format: prefix.field)
 		 */
 		function LoadValues(&$event, $store_field, $source_field)
 		{
 			$object =& $event->getObject();
 			/* @var $object kDBItem */
 
 			list ($sub_prefix, $sub_prefix_field) = explode('.', $source_field);
 			$foreign_key = $this->Application->getUnitOption($sub_prefix, 'ForeignKey');
 
 			$sql = 'SELECT '.$sub_prefix_field.'
 					FROM '.$this->getTable($sub_prefix, $object->IsTempTable()).'
 					WHERE '.$foreign_key.' = '.$object->GetID();
 
 			$constrain = $this->_getConstrain($object, $store_field, 'load');
 			if ($constrain) {
 				$sql .= ' AND (' . $sub_prefix_field . ' IN (' . $constrain . '))';
 			}
 
 			$selected_items = array_unique($this->Conn->GetCol($sql));
 
 			$object->SetDBField($store_field, $selected_items ? '|'.implode('|', $selected_items).'|' : '');
 		}
 
 		/**
 		 * Saves value to subitem's table
 		 *
 		 * @param kEvent $sub_event
 		 * @param string $store_field main item's field name, to get values from
 		 * @param string $sub_prefix_field check already existing records by this field
 		 */
 		function SaveValues(&$sub_event, $store_field, $sub_prefix_field)
 		{
 			$main_object =& $sub_event->MasterEvent->getObject();
 			/* @var $main_object kDBItem */
 
 			$affected_field = $main_object->GetDBField($store_field);
 
 			$object =& $this->Application->recallObject($sub_event->getPrefixSpecial(), null, Array('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$sub_table = $object->TableName;
 			$foreign_key = $this->Application->getUnitOption($sub_event->Prefix, 'ForeignKey');
 
 			// 1. get previous values from db
 			$sql = 'SELECT ' . $sub_prefix_field . '
 					FROM ' . $sub_table . '
 					WHERE '.$foreign_key.' = '.$main_object->GetID();
 
 			$constrain = $this->_getConstrain($main_object, $store_field, 'save');
 			if ($constrain) {
 				$sql .= ' AND (' . $sub_prefix_field . ' IN (' . $constrain . '))';
 			}
 
 			$old_values = $this->Conn->GetCol($sql);
 
 			// 2. get new values from form
 			$new_values = $affected_field ? explode('|', substr($affected_field, 1, -1)) : Array ();
 
 			$records_to_add = array_diff($new_values, $old_values);
 			$records_to_delete = array_diff($old_values, $new_values);
 
 			if ($records_to_delete && $main_object->Loaded) {
 				$where_clause = Array (
 					$foreign_key . ' = ' . $main_object->GetID(),
 					$sub_prefix_field . ' IN (' . implode(',', $records_to_delete) . ')',
 				);
 				$sql = 'SELECT ' . $object->IDField . '
 						FROM ' . $sub_table . '
 						WHERE (' . implode(') AND (', $where_clause) . ')';
 				$delete_ids = $this->Conn->GetCol($sql);
 
 				foreach ($delete_ids as $delete_id) {
 					$object->Delete($delete_id);
 				}
 			}
 
 			if ($records_to_add) {
 				$main_id = $main_object->GetID();
 				foreach ($records_to_add as $add_id) {
 					$object->Clear();
 					$object->SetDBField($foreign_key, $main_id);
 					$object->SetDBField($sub_prefix_field, $add_id);
 					$object->Create();
-
-					if ($object->IsTempTable()) {
-						$object->setTempID();
-					}
 				}
 			}
 		}
 
 		function _getConstrain(&$object, $store_field, $mode = 'filter')
 		{
 			$field_options = $object->GetFieldOptions($store_field);
 			$constrain = array_key_exists('option_constrain', $field_options) ? $field_options['option_constrain'] : false;
 
 			if ($mode == 'filter') {
 				// filter on edit form
 				return $constrain;
 			}
 			elseif ($constrain) {
 				// load or save
 				return sprintf($field_options['options_sql'], $field_options['option_key_field']);
 			}
 
 			return false;
 		}
 	}
\ No newline at end of file
Index: branches/5.0.x/core/units/helpers/brackets_helper.php
===================================================================
--- branches/5.0.x/core/units/helpers/brackets_helper.php	(revision 13602)
+++ branches/5.0.x/core/units/helpers/brackets_helper.php	(revision 13603)
@@ -1,470 +1,468 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class kBracketsHelper extends kHelper {
 
 		/**
 		 * Field name holding minimal amount
 		 *
 		 * @var string
 		 */
 		var $min_field = '';
 
 		/**
 		 * Field name holding maximal amount
 		 *
 		 * @var string
 		 */
 		var $max_field = '';
 
 		/**
 		 * Default values to be set to automtically created price brackets
 		 *
 		 * @var Array
 		 */
 		var $default_values = Array();
 
 		var $defaultStartValue = 1;
 
 		/**
 		 * Decimal separator
 		 *
 		 * @var string
 		 */
 		var $_decimalSeparator = '';
 
 		/**
 		 * Thousands separator
 		 *
 		 * @var string
 		 */
 		var $_thousandsSeparator = '';
 
 		/**
 		 * Current language
 		 *
 		 * @var LanguagesItem
 		 */
 		var $_language = null;
 
 		function kBracketsHelper()
 		{
 			parent::kHelper();
 
 			$this->_language =& $this->Application->recallObject('lang.current');
 			/* @var $lang kDBItem */
 
 			$this->_decimalSeparator = $this->_language->GetDBField('DecimalPoint');
 			$this->_thousandsSeparator = $this->_language->GetDBField('ThousandSep');
 		}
 
 		function InitHelper($min_field, $max_field, $default_values, $default_start_value = null)
 		{
 			$this->min_field = $min_field;
 			$this->max_field = $max_field;
 			$this->default_values = $default_values;
 
 			if (isset($default_start_value)) {
 				$this->defaultStartValue = $default_start_value;
 			}
 		}
 
 		/**
 		 * Converts number to operatable form
 		 *
 		 * @param string $value
 		 * @return float
 		 */
 		function _parseNumber($value)
 		{
 			$value = str_replace($this->_thousandsSeparator, '', $value);
 			$value = str_replace($this->_decimalSeparator, '.', $value);
 
 			return $value;
 		}
 
 		/**
 		 * Returns brackets from form with all numbers parsed
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 */
 		function getBrackets(&$event)
 		{
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 
 			return $this->parseBrackets($items_info);
 		}
 
 		function parseBrackets($brackets)
 		{
 			if (!$brackets) {
 				return $brackets;
 			}
 
 			foreach ($brackets as $id => $field_values) {
 				if (strlen($brackets[$id][$this->min_field])) {
 					$brackets[$id][$this->min_field] = (float)$this->_parseNumber($brackets[$id][$this->min_field]);
 				}
 
 				if (strlen($brackets[$id][$this->max_field])) {
 					$brackets[$id][$this->max_field] = (float)$this->_parseNumber($brackets[$id][$this->max_field]);
 				}
 			}
 
 			return $brackets;
 		}
 
 		/**
 		 * Formats given brackets and sets them back to request
 		 *
 		 * @param kEvent $event
 		 * @param Array $brackets
 		 */
 		function setBrackets(&$event, $brackets)
 		{
 			$brackets = $this->formatBrackets($brackets);
 
 			$this->Application->SetVar($event->getPrefixSpecial(true), $brackets);
 		}
 
 		function formatBrackets($brackets)
 		{
 			if (!$brackets) {
 				return $brackets;
 			}
 
 			foreach ($brackets as $id => $field_values) {
 				if (strlen($brackets[$id][$this->min_field])) {
 					$brackets[$id][$this->min_field] = $this->_language->formatNumber($brackets[$id][$this->min_field]);
 				}
 
 				if (strlen($brackets[$id][$this->max_field])) {
 					$brackets[$id][$this->max_field] = $this->_language->formatNumber($brackets[$id][$this->max_field]);
 				}
 			}
 
 			return $brackets;
 		}
 
 		/**
 		 * Adds 5 more empty brackets to brackets
 		 *
 		 * @param kEvent $event
 		 */
 		function OnMoreBrackets(&$event)
 		{
 			$field_values = $this->getBrackets($event);
 
 			$object =& $event->getObject();
 
 			foreach($field_values as $id => $record)
 			{
 				if($record[$this->max_field] == '&#8734;') $field_values[$id][$this->max_field] = -1;
 			}
 
 			$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName);
 			if($new_id > 0) $new_id = 0;
 			do
 			{
 				$new_id--;
 			}while( $this->arraySearch($field_values, $object->IDField, $new_id) );
 
 
 			$last_max_qty = $this->Conn->GetOne('SELECT MAX('.$this->max_field.') FROM '.$object->TableName);
 			$min_qty = $this->Conn->GetOne('SELECT MIN('.$this->max_field.') FROM '.$object->TableName);
 
 			if($min_qty == -1) $last_max_qty = -1;
 			if(!$last_max_qty) $last_max_qty = $this->defaultStartValue;
 
 
 			for($i = $new_id; $i > $new_id - 5; $i--)
 			{
 				$field_values[$i][$object->IDField] = $i;
 				$field_values[$i][$this->min_field] = ($i == $new_id-4 && $last_max_qty != -1) ? $last_max_qty : '';
 				$field_values[$i][$this->max_field] = ($i == $new_id-4 && $last_max_qty != -1) ? -1 : '';
 				$field_values[$i] = array_merge_recursive2($field_values[$i], $this->default_values);
 			}
 
 			$event->CallSubEvent('OnPreSaveBrackets');
 
 			$this->setBrackets($event, $field_values);
 		}
 
 		/**
 		 * Adds infinity bracket
 		 *
 		 * @param kEvent $event
 		 */
 		function OnInfinity(&$event)
 		{
 			$object =& $event->getObject();
 			$infinite_exists = $this->Conn->GetOne('SELECT COUNT(*) FROM '.$object->TableName.' WHERE '.$this->max_field.' = -1');
 			$field_values = $this->getBrackets($event);
 			/*if(is_array($field_values))
 			{
 				foreach($field_values as $values)
 				{
 					$infinite_exists = $infinite_exists || ($values[$this->max_field] == -1);
 				}
 			}*/
 
 			if ($infinite_exists == 0) {
 				reset($field_values);
 				$last_bracket = end($field_values);
 				$new_id = (int)$this->Conn->GetOne('SELECT MIN('.$object->IDField.') FROM '.$object->TableName);
 
 				$brackets_exist = (int)$this->Conn->GetOne('SELECT COUNT(*) FROM '.$object->TableName);
 
 				if($new_id > 0) $new_id = 0;
 				do
 				{
 					$new_id--;
 				}while( $this->arraySearch($field_values, $object->IDField, $new_id) );
 
 				$infinite_bracket[$object->IDField] = $new_id;
 				$infinite_bracket[$this->min_field] = ($brackets_exist > 0) ? $last_bracket[$this->max_field] : $this->defaultStartValue;
 				$infinite_bracket[$this->max_field] = '-1';
 				$infinite_bracket = array_merge_recursive2($infinite_bracket, $this->default_values);
 
 				$field_values[$new_id] = $infinite_bracket;
 				reset($field_values);
 
 				$this->setBrackets($event, $field_values);
 			}
 		}
 
 		/**
 		 * Saves brackets to database
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPreSaveBrackets(&$event)
 		{
 			$items_info = $this->getBrackets($event);
 			if ($items_info) {
 				$object =& $event->getObject();
 
 				$linked_info = $object->getLinkedInfo();
 				$stored_ids = $this->Conn->GetCol('SELECT '.$object->IDField.' FROM '.$object->TableName.' WHERE '.$linked_info['ForeignKey'].' = '.$linked_info['ParentId']);
 
 				uasort($items_info, Array(&$this, 'compareBrackets') );
 
 				foreach ($items_info as $item_id => $values) {
 
 					if (in_array($item_id, $stored_ids)) { //if it's already exist
 						$object->SetDefaultValues();
 						$object->Load($item_id);
 						$object->SetFieldsFromHash($values);
 						if (!$object->Validate()) {
 							unset($stored_ids[array_search($item_id, $stored_ids)]);
 							$event->redirect = false;
 							continue;
 						}
 						if( $object->Update($item_id) )
 						{
 							$event->status = erSUCCESS;
 						}
 						else
 						{
 							$event->status = erFAIL;
 							$event->redirect = false;
 							break;
 						}
 						unset( $stored_ids[ array_search($item_id, $stored_ids) ] );
 					}
 					else {
 						$object->SetDefaultValues();
 						$object->SetFieldsFromHash($values);
 						$object->SetDBField($linked_info['ForeignKey'], $linked_info['ParentId']);
 
-						if( $object->Create() )
-						{
-							$object->setTempID();
+						if ($object->Create()) {
 							$event->status = erSUCCESS;
 						}
 					}
 				}
 
 				// delete
 				foreach ($stored_ids as $stored_id)
 				{
 					$this->Conn->Query('DELETE FROM '.$object->TableName.' WHERE '.$object->IDField.' = '.$stored_id);
 				}
 
 			}
 		}
 
 		function arrangeBrackets(&$event)
 		{
 			$object =& $event->getObject();
 
 			$temp = $this->getBrackets($event);
 
 			foreach($temp as $id => $record)
 			{
 				if( $record[$this->max_field] == '&#8734;' )
 				{
 					$temp[$id][$this->max_field] = -1;
 				}
 			}
 
 			$temp_orig = $temp;
 			reset($temp);
 			if( is_array($temp) )
 			{
 				// array to store max values (2nd column)
 				$end_values = Array();
 
 				// get minimal value of Min
 				$first_elem = current($temp);
 				$start = $first_elem[$this->min_field];
 				if (!strlen($start)) {
 					$start = $this->defaultStartValue;
 				}
 
 				foreach($temp as $id => $record)
 				{
 					if(
 					// MAX is less than start
 					($record[$this->max_field] <= $start && $record[$this->max_field] != -1) ||
 					// Max is empty
 					!strlen($record[$this->max_field]) ||
 					// Max already defined in $end_values
 					(array_search($record[$this->max_field], $end_values) !== false)
 					) {	// then delete from brackets list
 						unset($temp[$id]);
 					}
 					else {	// this is when ok - add to end_values list
 						$end_values[] = $record[$this->max_field];
 					}
 				}
 
 				// sort brackets by 2nd column (Max values)
 				uasort($temp, Array(&$this, 'compareBrackets') );
 				reset($temp);
 				$first_item = each($temp);
 				$first_item_key = $first_item['key'];
 
 				$linked_info = $object->getLinkedInfo();
 				$sql = 'SELECT %s FROM %s WHERE %s = %s';
 				$ids = $this->Conn->GetCol( sprintf($sql, $object->IDField, $object->TableName, $linked_info['ForeignKey'], $linked_info['ParentId']) );
 				if( is_array($ids) )
 				{
 					usort($ids, Array(&$this, 'sortBracketIDs') );
 				}
 
 //				$min_id = min( min($ids) - 1, -1 );
 
 				foreach($temp as $key => $record)
 				{
 					$temp[$key][$this->min_field] = $start;
 					$start = $temp[$key][$this->max_field];
 				}
 			}
 
 			$this->setBrackets($event, $temp);
 
 			return $temp;
 		}
 
 		function compareBrackets($bracket1, $bracket2) // ap_bracket_comp
 		{
 			$bracket1_min = $bracket1[$this->min_field];
 			$bracket1_max = $bracket1[$this->max_field];
 
 			$bracket2_min = $bracket2[$this->min_field];
 			$bracket2_max = $bracket2[$this->max_field];
 
 			// limits
 			if( ($bracket1_min != '') && ($bracket1_max == '') && ($bracket2_min != '') && ($bracket2_max != '') ) return 1;
 			if( ($bracket1_min != '') && ($bracket1_max == '') && ($bracket2_min == '') && ($bracket2_max == '') ) return -1;
 			if( ($bracket1_max == '') && ($bracket2_max != '') ) return 1;
 			if( ($bracket1_max != '') && ($bracket2_max == '') ) return -1;
 
 
 			if( ( ($bracket1_max > $bracket2_max) && ($bracket2_max != -1) ) || ( ($bracket1_max == -1) && ($bracket2_max != -1) ) )
 			{
 				return 1;
 			}
 			elseif( ($bracket1_max < $bracket2_max) || ( ($bracket2_max == -1) && ($bracket1_max != -1) ) )
 			{
 				return -1;
 			}
 			else
 			{
 				return 0;
 			}
 		}
 
 		function sortBracketIDs($first_id, $second_id) // pr_bracket_id_sort
 		{
 			$first_abs = abs($first_id);
 			$second_abs = abs($second_id);
 
 			$first_sign = ($first_id == 0) ? 0 : $first_id / $first_abs;
 			$second_sign = ($second_id == 0) ? 0 : $second_id / $second_abs;
 
 			if($first_sign != $second_sign)
 			{
 				if($first_id > $second_id)
 				{
 					$bigger =& $first_abs;
 					$smaller =& $second_abs;
 				}
 				else
 				{
 					$bigger =& $second_abs;
 					$smaller =& $first_abs;
 				}
 				$smaller = $bigger + $smaller;
 			}
 
 			return ($first_abs > $second_abs) ? 1 : ($first_abs < $second_abs ? -1 : 0);
 		}
 
 		/**
 		 * Searches through submitted grid data to find record with specific value in specific field
 		 *
 		 * @param Array $records // grid data from REQUEST
 		 * @param string $field
 		 * @param string $value
 		 * @return bool
 		 */
 		function arraySearch($records, $field, $value) // check_array
 		{
 			foreach ($records as $record)
 			{
 				if ($record[$field] == $value)
 				{
 					return true;
 				}
 			}
 			return false;
 		}
 
 		/**
 		 * Replate infinity mark with -1 before saving to db
 		 *
 		 * @param kEvent $event
 		 */
 		function replaceInfinity(&$event)
 		{
 			$object =& $event->getObject();
 			if($object->GetDBField($this->max_field) == '&#8734;') $object->SetDBField($this->max_field, -1);
 		}
 
 	}
\ No newline at end of file
Index: branches/5.0.x/core/units/selectors/selectors_event_handler.php
===================================================================
--- branches/5.0.x/core/units/selectors/selectors_event_handler.php	(revision 13602)
+++ branches/5.0.x/core/units/selectors/selectors_event_handler.php	(revision 13603)
@@ -1,380 +1,378 @@
 <?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!');
 
 safeDefine('STYLE_BASE', 	1);
 safeDefine('STYLE_BLOCK', 	2);
 
 	class SelectorsEventHandler extends kDBEventHandler
 	{
 		/**
 		 * Allows to override standart permission mapping
 		 *
 		 */
 		function mapPermissions()
 		{
 			parent::mapPermissions();
 			$permissions = Array(
 									'OnResetToBase'		=>	Array('subitem' => 'add|edit'),
 									'OnMassResetToBase'	=>	Array('subitem' => 'add|edit'),
 
 									'OnOpenStyleEditor'	=>	Array('subitem' => 'add|edit'),
 									'OnSaveStyle'		=>	Array('subitem' => 'add|edit'),
 							);
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Occures before an item has been cloned
 		 * Id of newly created item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeClone(&$event)
 		{
 			$event->Init($event->Prefix, '-item');
 			$object =& $event->getObject();
 
 			$title_field = 'SelectorName';
 			$new_name = $object->GetDBField($title_field);
 			$original_checked = false;
 
 			$foreign_key = $event->getEventParam('foreign_key'); // in case if whole stylesheet is cloned
 			if($foreign_key === false) $foreign_key = $object->GetDBField('StylesheetId'); // in case if selector is copied ifself
 
 			do {
 				if ( preg_match('/(.*)-([\d]+)/', $new_name, $regs) ) {
 					$new_name = $regs[1].'-'.($regs[2]+1);
 				}
 				elseif ($original_checked) {
 					$new_name = $new_name.'-1';
 				}
 
 				// if we are cloning in temp table this will look for names in temp table,
 				// since object' TableName contains correct TableName (for temp also!)
 				// if we are cloning live - look in live
 				$query = '	SELECT '.$title_field.'
 							FROM '.$object->TableName.'
 							WHERE '.$title_field.' = '.$this->Conn->qstr($new_name).' AND StylesheetId = '.$foreign_key;
 
 				$res = $this->Conn->GetOne($query);
 
 				/*// if not found in live table, check in temp table if applicable
 				if ($res === false && $object->Special == 'temp') {
 					$query = 'SELECT '.$name_field.' FROM '.$this->GetTempName($master['TableName']).'
 										WHERE '.$name_field.' = '.$this->Conn->qstr($new_name);
 					$res = $this->Conn->GetOne($query);
 				}*/
 
 				$original_checked = true;
 			} while ($res !== false);
 			$object->SetDBField($title_field, $new_name);
 		}
 
 		/**
 		 * Show base styles or block styles
 		 *
 		 * @param kEvent $event
 		 */
 		function SetCustomQuery(&$event)
 		{
 			$object =& $event->getObject();
 			switch ($event->Special)
 			{
 				case 'base':
 					$object->addFilter('type_filter', '%1$s.Type = 1');
 					break;
 
 				case 'block':
 					$object->addFilter('type_filter', '%1$s.Type = 2');
 					break;
 			}
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemUpdate(&$event)
 		{
 			$this->SerializeSelectorData($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnBeforeItemCreate(&$event)
 		{
 			$this->SerializeSelectorData($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemUpdate(&$event)
 		{
 			$this->UnserializeSelectorData($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemCreate(&$event)
 		{
 			$this->UnserializeSelectorData($event);
 		}
 
 		/**
 		 * Get's special of main item for linking with subitem
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 */
 		function getMainSpecial(&$event)
 		{
 			return '';
 		}
 
 		/**
 		 * Save css-style name & description before opening css editor
 		 *
 		 * @param kEvent $event
 		 */
 		function OnOpenStyleEditor(&$event)
 		{
 			$this->SaveChanges($event);
 			$event->redirect = false;
 		}
 
 		/**
 		 * Saves Changes to Item
 		 *
 		 * @param kEvent $event
 		 */
 		function SaveChanges(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info)
 			{
 				list($id,$field_values) = each($items_info);
-				if($id == 0)
-				{
+				if ($id == 0) {
 					$parent_id = getArrayValue($field_values,'ParentId');
 					if($parent_id) $object->Load($parent_id);
 
 					$object->SetFieldsFromHash($field_values);
 					$object->Create();
-					if( $object->IsTempTable() ) $object->setTempID();
+
 					$this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetID() );
 				}
-				else
-				{
+				else {
 					$object->Load($id);
 					$object->SetFieldsFromHash($field_values);
 					$object->Update();
 				}
 			}
 		}
 
 		/**
 		 * Save style changes from style editor
 		 *
 		 * @param kEvent $event
 		 */
 		function OnSaveStyle(&$event)
 		{
 			$this->SaveChanges($event);
 
 			$object =& $event->getObject();
 			$this->Application->SetVar($event->getPrefixSpecial().'_id', $object->GetId() );
 
 			$this->finalizePopup($event);
 		}
 
 		/**
 		 * Extract styles
 		 *
 		 * @param kEvent $event
 		 */
 		function OnAfterItemLoad(&$event)
 		{
 			$object =& $event->getObject();
 			$selector_data = $object->GetDBField('SelectorData');
 			if($selector_data)
 			{
 				$selector_data = unserialize($selector_data);
 				$object->SetDBField('SelectorData', $selector_data);
 			}
 			else
 			{
 				$selector_data = Array();
 			}
 			$this->AddParentProperties($event, $selector_data);
 		}
 
 		/**
 		 * Serialize item before saving to db
 		 *
 		 * @param kEvent $event
 		 */
 		function SerializeSelectorData(&$event)
 		{
 			$object =& $event->getObject();
 			$selector_data = $object->GetDBField('SelectorData');
 			if(!$selector_data) $selector_data = Array();
 
 			$selector_data = $this->RemoveParentProperties($event, $selector_data);
 
 			if( !IsSerialized($selector_data) ) $selector_data = serialize($selector_data);
 
 			$object->SetDBField('SelectorData', $selector_data);
 		}
 
 		/**
 		 * Unserialize data back when update was made
 		 *
 		 * @param kEvent $event
 		 */
 		function UnserializeSelectorData(&$event)
 		{
 			$object =& $event->getObject();
 			$selector_data = $object->GetDBField('SelectorData');
 			if(!$selector_data) $selector_data = Array();
 			if( IsSerialized($selector_data) ) $selector_data = unserialize($selector_data);
 
 			$selector_data = $this->AddParentProperties($event, $selector_data);
 
 			$object->SetDBField('SelectorData', $selector_data);
 		}
 
 		/**
 		 * Populate options based on temporary table :)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnPrepareBaseStyles(&$event)
 		{
 			$object =& $event->getObject();
 
 			$parent_info = $object->getLinkedInfo();
 			$title_field = $this->Application->getUnitOption($event->Prefix,'TitleField');
 			$sql = 'SELECT '.$title_field.', '.$object->IDField.' FROM '.$object->TableName.' WHERE Type = 1 AND StylesheetId = '.$parent_info['ParentId'].' ORDER BY '.$title_field;
 			$object->Fields['ParentId']['options'] = $this->Conn->GetCol($sql,$object->IDField);
 		}
 
 		/**
 		 * Remove properties of parent style that match by value from style
 		 *
 		 * @param kEvent $event
 		 */
 		function RemoveParentProperties(&$event, $selector_data)
 		{
 			$object =& $event->getObject();
 			$parent_id = $object->GetDBField('ParentId');
 			if($parent_id)
 			{
 				$sql = 'SELECT SelectorData FROM '.$object->TableName.' WHERE '.$object->IDField.' = '.$parent_id;
 				$base_selector_data = $this->Conn->GetOne($sql);
 
 				if( IsSerialized($base_selector_data) ) $base_selector_data = unserialize($base_selector_data);
 
 				foreach($selector_data as $prop_name => $prop_value)
 				{
 					if( !$prop_value || getArrayValue($base_selector_data,$prop_name) == $prop_value )
 					{
 						unset($selector_data[$prop_name]);
 					}
 				}
 				$object->SetDBField('SelectorData', $selector_data);
 				return $selector_data;
 			}
 			else
 			{
 				foreach($selector_data as $prop_name => $prop_value)
 				{
 					if(!$prop_value) unset($selector_data[$prop_name]);
 				}
 				$object->SetDBField('SelectorData', $selector_data);
 				return $selector_data;
 			}
 		}
 
 		/**
 		 * Add back properties from parent style, that match this style property values
 		 *
 		 * @param kEvent $event
 		 */
 		function AddParentProperties(&$event, $selector_data)
 		{
 			$object =& $event->getObject();
 			$parent_id = $object->GetDBField('ParentId');
 			if($parent_id)
 			{
 				$sql = 'SELECT SelectorData FROM '.$object->TableName.' WHERE '.$object->IDField.' = '.$parent_id;
 				$base_selector_data = $this->Conn->GetOne($sql);
 
 				if( IsSerialized($base_selector_data) ) $base_selector_data = unserialize($base_selector_data);
 
 				$selector_data = array_merge_recursive2($base_selector_data,$selector_data);
 
 				$object->SetDBField('SelectorData', $selector_data);
 				return $selector_data;
 			}
 			return $selector_data;
 		}
 
 		/**
 		 * Reset Style definition to base style -> no customizations
 		 *
 		 * @param kEvent $event
 		 */
 		function OnResetToBase(&$event)
 		{
 			$object =& $event->getObject();
 			$object->SetFieldsFromHash( $this->getSubmittedFields($event) );
 			$object->ResetStyle();
 
 			$event->redirect_params['pass'] = 'all,'.$event->getPrefixSpecial();
 		}
 
 		/**
 		 * Resets selected styles properties to values of their base classes
 		 *
 		 * @param kEvent $event
 		 */
 		function OnMassResetToBase(&$event)
 		{
 			$object =& $event->getObject( Array('skip_autoload' => true) );
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 			if($items_info)
 			{
 				foreach($items_info as $id => $field_values)
 				{
 					$object->Load($id);
 	 				$object->ResetStyle();
 				}
 			}
 		}
 	}
\ No newline at end of file