Index: trunk/core/kernel/utility/temp_handler.php =================================================================== diff -u -N --- trunk/core/kernel/utility/temp_handler.php (revision 8090) +++ trunk/core/kernel/utility/temp_handler.php (revision 0) @@ -1,718 +0,0 @@ -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) - { - $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); - } - - $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'], $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']; - - $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'], '', $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); - - // 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); - - $this->CopiedTables[] = $table_sig; - /* - - !!! 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']); - $this->Application->resetCounters($master['TableName']); - - if (!isset($this->savedIDs[ $master['Prefix'] ])) { - $this->savedIDs[ $master['Prefix'] ] = Array(); - } - - return $this->savedIDs[ $master['Prefix'] ]; - } - - function UpdateForeignKeys($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) return; - - 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); - } - - foreach($ids as $id) - { - $event->setEventParam('id', $id); - 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']); - } - - function SaveEdit($master_ids = Array()) - { - return $this->DoCopyTempToOriginal($this->Tables, null, $master_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); - } - } - } -} - -?> \ No newline at end of file