Index: trunk/core/kernel/utility/temp_handler.php
===================================================================
--- trunk/core/kernel/utility/temp_handler.php	(revision 2796)
+++ trunk/core/kernel/utility/temp_handler.php	(revision 2797)
@@ -1,597 +1,598 @@
 <?php
 
 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();
 
 	/**
 	* Description
 	*
 	* @var kDBConnection
 	* @access public
 	*/
 	var $Conn;
 
 	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'];
 	}
 
 	/**
 	 * Get temp table name
 	 *
 	 * @param string $table
 	 * @return string
 	 */
 	function GetTempName($table)
 	{
 		// function is sometimes called as static, so we CAN'T use $this->GetTempTablePrefix() here
 		return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_'.$table;
 	}
 
 	function GetTempTablePrefix()
 	{
 		return TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_';
 	}
 
 	/**
 	 * Return live table name based on temp table name
 	 *
 	 * @param string $temp_table
 	 * @return string
 	 */
 	function GetLiveName($temp_table)
 	{
 		if( preg_match('/'.TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_(.*)/',$temp_table,$rets) )
 		{
 			return $rets[1];
 		}
 		else
 		{
 			return $temp_table;
 		}
 	}
 
 	function IsTempTable($table)
 	{
 		return strpos($table, TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_edit_') !== false;
 	}
 
 	/**
 	 * 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->TableIdCounter = 0;
 		$tables = Array(
 				'TableName' => $this->Application->getUnitOption($prefix,'TableName'),
 				'IdField' => $this->Application->getUnitOption($prefix,'IDField'),
 				'IDs' => $ids,
 				'Prefix' => $prefix,
 				'TableId' => $this->TableIdCounter++,
 		);
 
 		$SubItems = $this->Application->getUnitOption($prefix,'SubItems');
 		if (is_array($SubItems)) {
 			foreach ($SubItems as $prefix) {
 				$this->AddTables($prefix, $tables);
 			}
 		}
 		$this->SetTables($tables);
 	}
 
 	function AddTables($prefix, &$tables)
 	{
 		$tmp = Array(
 				'TableName' => $this->Application->getUnitOption($prefix,'TableName'),
 				'IdField' => $this->Application->getUnitOption($prefix,'IDField'),
 				'ForeignKey' => $this->Application->getUnitOption($prefix,'ForeignKey'),
 				'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;
 
 		$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)
 	{
 		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';
 		$this->Application->setUnitOption($prefix, 'AutoLoad', false);
 
 		$object =& $this->Application->recallObject($recall_prefix, $prefix);
 
 		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;
 
 			$object->NameCopy($master, $foreign_key);
 
 			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'], Array($object->GetId()) );
 			}
 
 			$res = $mode == 'update' ? $object->Update() : $object->Create();
 
 			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'], Array($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'];
 
 						$query = 'SELECT '.$sub_table['IdField'].' FROM '.$sub_TableName.'
 											WHERE '.$foreign_key_field.' = '.$original_values[$parent_key_field];
 
 						$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'], '', $sub_ids, $sub_table, $parent_key, $master['Prefix']);
 					}
 				}
 			}
 		}
 	}
 
 	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';
 		$this->Application->setUnitOption($prefix,'AutoLoad',false);
 		$object =& $this->Application->recallObject($recall_prefix, $prefix);
 
 		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']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey'];
 					$parent_key_field = is_array($sub_table['ParentTableKey']) ? $sub_table['ParentTableKey'][$master['Prefix']] : $sub_table['ParentTableKey'];
 
 					$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($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 ( $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)) {
 				$query = 'SELECT '.$parent_key_field.' FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$master['IdField'].' IN ('.join(',', $temp_id).')';
 				$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)
 	{
 		$query = 'SELECT '.$master['IdField'].' FROM '.$this->GetTempName($master['TableName']);
 		if (isset($master['Constrain'])) $query .= ' WHERE '.$master['Constrain'];
 		$current_ids = $this->Conn->GetCol($query);
 
 		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') ) {
 				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;
 						$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->RaiseEvent( 'OnAfterCopyToLive', $master['Prefix'], Array($insert_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 .= ' WHERE '.$master['Constrain'];
+					if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 					$this->Conn->Query($query);
 				}
 				// when all of ids in current master has been processed, copy all sub-tables data
 				$this->CopySubTablesToLive($master, $current_ids);
 			}
 			else { //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);
 
 				// reset ALL negative IDs to 0 so it get inserted into live table with autoincrement
 				$query = 'UPDATE '.$this->GetTempName($master['TableName']).'
 									SET '.$master['IdField'].' = 0
 									WHERE '.$master['IdField'].' < 0';
 				if (isset($master['Constrain'])) $query .= ' AND '.$master['Constrain'];
 				$this->Conn->Query($query);
 
 				// 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);
 
 				/*
 
 				!!! WE NEED TO FIND A WAY TO DETERMINE IF OnAfterCopyToLive is not an empty method, and do on-by-one insert
 				and pass Ids to OnAfterCopyToLive, otherwise it's not smart to do on-by-one insert for any object
 				OR WE COULD FIND A WAY TO GET ALL INSERTED IDS as an array and iterate them !!!
 
 				$this->RaiseEvent('OnAfterCopyToLive', 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']);
 	}
 
 	function UpdateForeignKeys($master, $live_id, $temp_id) {
 		foreach ($master['SubTables'] as $sub_table) {
 			list ($live_foreign_key, $temp_foreign_key) = $this->GetForeignKeys($master, $sub_table, $live_id, $temp_id);
 
 			$foreign_key_field =  is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey'];
 
 			//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) {
 
 			// delete records from live table by foreign key, so that records deleted from temp table
 			// get deleted from live
 			if (count($current_ids) > 0) {
 				$foreign_keys = $this->GetForeignKeys($master, $sub_table, $current_ids);
 				$foreign_key_field =  is_array($sub_table['ForeignKey']) ? $sub_table['ForeignKey'][$master['Prefix']] : $sub_table['ForeignKey'];
 				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'];
 
 					$this->RaiseEvent( 'OnBeforeDeleteFromLive', $sub_table['Prefix'], $this->Conn->GetCol($query) );
 
 					$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, $ids)
 	{
 		if ( !is_array($ids) ) return;
 		foreach ($ids as $id) {
 			$event = new kEvent( Array('name'=>$name, 'prefix'=>$prefix, 'special'=>'') );
 			$event->setEventParam('id', $id);
 			$this->Application->HandleEvent($event);
 		}
 	}
 
 	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']);
 	}
 
 	function SaveEdit($skip_master=0)
 	{
 		$this->DoCopyTempToOriginal($this->Tables);
 	}
 
 	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);
 			}
 		}
 	}
 }
 
 ?>
\ No newline at end of file

Property changes on: trunk/core/kernel/utility/temp_handler.php
___________________________________________________________________
Modified: cvs2svn:cvs-rev
## -1 +1 ##
-1.11
\ No newline at end of property
+1.12
\ No newline at end of property