Index: branches/5.1.x/core/kernel/db/dbitem.php =================================================================== diff -u -N -r13086 -r13161 --- branches/5.1.x/core/kernel/db/dbitem.php (.../dbitem.php) (revision 13086) +++ branches/5.1.x/core/kernel/db/dbitem.php (.../dbitem.php) (revision 13161) @@ -1,6 +1,6 @@ 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) { + $this->setModifiedFlag(clDELETE); // will change affected rows, so get it before this line + // something was actually deleted $this->raiseEvent('OnAfterItemDelete'); } @@ -1063,18 +1063,31 @@ $this->Conn->Query('UPDATE '.$this->TableName.' SET `'.$this->IDField.'` = '.$new_id.' WHERE `'.$this->IDField.'` = '.$this->GetID()); - if ($this->ShouldLogChanges()) { + if ($this->ShouldLogChanges(true)) { // Updating TempId in ChangesLog, if changes are disabled - $ses_var_name = $this->Application->GetTopmostPrefix($this->Prefix).'_changes_'.$this->Application->GetTopmostWid($this->Prefix); + $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()) { + // change log for record, that's ID was just updated -> update in change log record too $changes[$key]['ItemId'] = $new_id; } + + if ($rec['MasterPrefix'] == $this->Prefix && $rec['MasterId'] == $this->GetID()) { + // master item id was changed + $changes[$key]['MasterId'] = $new_id; + } + + if (in_array($this->Prefix, $rec['ParentPrefix']) && $rec['ParentId'][$this->Prefix] == $this->GetID()) { + // change log record of given item's sub item + $changes[$key]['ParentId'][$this->Prefix] = $new_id; + } } } + $this->Application->StoreVar($ses_var_name, serialize($changes)); } @@ -1090,26 +1103,33 @@ function setModifiedFlag($mode = null) { $main_prefix = $this->Application->GetTopmostPrefix($this->Prefix); - $this->Application->StoreVar($main_prefix.'_modified', '1', !$this->Application->isAdmin); + $this->Application->StoreVar($main_prefix . '_modified', '1', !$this->Application->isAdmin); - if ($this->ShouldLogChanges()) { + if ($this->ShouldLogChanges(true)) { $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); + $handler =& $this->Application->recallObject($this->Prefix . '_EventHandler'); + /* @var $handler kDBEventHandler */ + + $ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); + $handler->SaveLoggedChanges($ses_var_name, $this->ShouldLogChanges()); } } } /** * Determines, that changes made to this item should be written to change log * + * @param bool $log_changes * @return bool */ - function ShouldLogChanges() + function ShouldLogChanges($log_changes = null) { - $log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog'); + if (!isset($log_changes)) { + // specific logging mode no forced -> use global logging settings + $log_changes = $this->Application->getUnitOption($this->Prefix, 'LogChanges') || $this->Application->ConfigValue('UseChangeLog'); + } return $log_changes && !$this->Application->getUnitOption($this->Prefix, 'ForceDontLogChanges'); } @@ -1120,69 +1140,209 @@ return ; } - $ses_var_name = $main_prefix.'_changes_'.$this->Application->GetTopmostWid($this->Prefix); + $ses_var_name = $main_prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix); $changes = $this->Application->RecallVar($ses_var_name); - $changes = $changes ? unserialize($changes) : array(); + $changes = $changes ? unserialize($changes) : Array (); - $general = array( + $fields_hash = 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, ); + + if ($this->Prefix == $main_prefix) { + // main item + $fields_hash['MasterId'] = $this->GetID(); + $fields_hash['ParentPrefix'] = Array ($main_prefix); + $fields_hash['ParentId'] = Array ($main_prefix => $this->GetID()); + } + else { + // sub item + // collect foreign key values (for serial reset) + $foreign_keys = $this->Application->getUnitOption($this->Prefix, 'ForeignKey'); + $fields_hash['ParentId'] = $fields_hash['ParentPrefix'] = Array (); + + if (is_array($foreign_keys)) { + foreach ($foreign_keys as $prefix => $field_name) { + $fields_hash['ParentPrefix'][] = $prefix; + $fields_hash['ParentId'][$prefix] = $this->getParentId($prefix); + } + } + else { + $fields_hash['ParentPrefix'] = Array ( $this->Application->getUnitOption($this->Prefix, 'ParentPrefix') ); + $fields_hash['ParentId'][ $fields_hash['ParentPrefix'][0] ] = $this->getParentId('auto'); + } + + // works only, when main item is present in url, when subitem is changed + $master_id = $this->Application->GetVar($main_prefix . '_id'); + + if ($master_id === false) { + // works in case of we are not editing topmost item, when subitem is created/updated/deleted + $master_id = $this->getParentId('auto', true); + } + + $fields_hash['MasterId'] = $master_id; + } + switch ($mode) { case clUPDATE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetChangedFields())), - )); + $to_save = array_merge($this->GetTitleField(), $this->GetChangedFields()); break; + case clCREATE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize($this->GetTitleField()), - )); + $to_save = $this->GetTitleField(); break; + case clDELETE: - $changes[] = array_merge($general, Array( - 'Changes' => serialize(array_merge($this->GetTitleField(), $this->GetRealFields())), - )); + $to_save = array_merge($this->GetTitleField(), $this->GetRealFields()); + break; } + $fields_hash['Changes'] = serialize($to_save); + $changes[] = $fields_hash; + $this->Application->StoreVar($ses_var_name, serialize($changes)); } + /** + * Returns current item parent's ID + * + * @param bool $top_most return topmost parent, when used + * @return int + */ + function getParentId($parent_prefix, $top_most = false) + { + $current_id = $this->GetID(); + $current_prefix = $this->Prefix; + + if ($parent_prefix == 'auto') { + $parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix'); + } + + if (!$parent_prefix) { + return $current_id; + } + + do { + // field in this table + $foreign_key = $this->Application->getUnitOption($current_prefix, 'ForeignKey'); + $foreign_key = is_array($foreign_key) ? $foreign_key[$parent_prefix] : $foreign_key; + + // get foreign key value for $current_prefix + if ($current_prefix == $this->Prefix) { + $foreign_key_value = $this->GetDBField($foreign_key); + } + else { + $id_field = $this->Application->getUnitOption($current_prefix, 'IDField'); + $table_name = $this->Application->getUnitOption($current_prefix, 'TableName'); + + if ($this->IsTempTable()) { + $table_name = $this->Application->GetTempName($table_name, 'prefix:' . $current_prefix); + } + + $sql = 'SELECT ' . $foreign_key . ' + FROM ' . $table_name . ' + WHERE ' . $id_field . ' = ' . $current_id; + $foreign_key_value = $this->Conn->GetOne($sql); + } + + // field in parent table + $parent_table_key = $this->Application->getUnitOption($current_prefix, 'ParentTableKey'); + $parent_table_key = is_array($parent_table_key) ? $parent_table_key[$parent_prefix] : $parent_table_key; + + $parent_id_field = $this->Application->getUnitOption($parent_prefix, 'IDField'); + $parent_table_name = $this->Application->getUnitOption($parent_prefix, 'TableName'); + + if ($this->IsTempTable()) { + $parent_table_name = $this->Application->GetTempName($parent_table_name, 'prefix:' . $current_prefix); + } + + if ($parent_id_field == $parent_table_key) { + // sub-item is related by parent item idfield + $current_id = $foreign_key_value; + } + else { + // sub-item is related by other parent item field + $sql = 'SELECT ' . $parent_id_field . ' + FROM ' . $parent_table_name . ' + WHERE ' . $parent_table_key . ' = ' . $foreign_key_value; + $current_id = $this->Conn->GetOne($sql); + } + + $current_prefix = $parent_prefix; + + if (!$top_most) { + break; + } + } while ( $parent_prefix = $this->Application->getUnitOption($current_prefix, 'ParentPrefix') ); + + return $current_id; + } + + /** + * Returns title field (if any) + * + * @return Array + */ 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)); + + if ($title_field) { + $value = $this->GetField($title_field); + return $value ? Array ($title_field => $value) : Array (); } + + return Array (); } + /** + * Returns only fields, that are present in database (no virtual and no calculated fields) + * + * @return Array + */ function GetRealFields() { if (function_exists('array_diff_key')) { $db_fields = array_diff_key($this->FieldValues, $this->VirtualFields, $this->CalculatedFields); } else { - $db_fields = array(); + $db_fields = Array(); + foreach ($this->FieldValues as $key => $value) { - if (array_key_exists($key, $this->VirtualFields) || array_key_exists($key, $this->CalculatedFields)) continue; + if (array_key_exists($key, $this->VirtualFields) || array_key_exists($key, $this->CalculatedFields)) { + continue; + } + $db_fields[$key] = $value; } } + return $db_fields; } + /** + * Returns only changed database field + * + * @return Array + */ function GetChangedFields() { - $changes = array(); - + $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)); + $old_value = $this->GetOriginalField($field, true); + $new_value = $this->GetField($field); + + if ($old_value != $new_value) { + // "0.00" and "0.0000" are stored as strings and will differ. Double check to prevent that. + $changes[$field] = Array ('old' => $old_value, 'new' => $new_value); + } } + return $changes; }