getSessionVariableName($main_prefix, $main_prefix); $this->Application->RemoveVar($ses_var_name); } /** * Updates the foreign keys in the changelog for a given table based on the provided identifiers. * * This method ensures that all references to the temporary ID in the changelog are updated to use * the live ID, while considering dependencies and relationships between items. * * @param array $table_info Table info. * @param integer $live_id Live ID. * @param integer $temp_id Temp ID. * @param string $caller_prefix Caller prefix. * * @return void */ public function updateForeignKeys(array $table_info, $live_id, $temp_id, $caller_prefix) { if ( $live_id == $temp_id ) { return; } $prefix = $table_info['Prefix']; $main_prefix = $this->Application->GetTopmostPrefix($prefix); $ses_var_name = $this->getSessionVariableName($main_prefix, $caller_prefix); $changes = $this->Application->RecallVar($ses_var_name); $changes = $changes ? unserialize($changes) : array(); if ( !$changes ) { return; } foreach ( $changes as $key => $rec ) { // The main item changelog record. if ( $rec['Prefix'] == $prefix && $rec['ItemId'] == $temp_id ) { $changes[$key]['ItemId'] = $live_id; } // The subitem changelog record. if ( $rec['MasterPrefix'] == $prefix && $rec['MasterId'] == $temp_id ) { $changes[$key]['MasterId'] = $live_id; } // The parent item changelog record. if ( in_array($prefix, $rec['ParentPrefix']) && $rec['ParentId'][$prefix] == $temp_id ) { $changes[$key]['ParentId'][$prefix] = $live_id; if ( array_key_exists('DependentFields', $rec) ) { /* * These are fields from the $rec['Prefix'] table! * Used, when one of the dependent fields uses IdField of its changed parent item. */ $parent_table_key = $this->Application->getUnitOption($rec['Prefix'], 'ParentTableKey'); $parent_table_key = is_array($parent_table_key) ? $parent_table_key[$prefix] : $parent_table_key; if ( $parent_table_key == $table_info['IdField'] ) { $foreign_key = $this->Application->getUnitOption($rec['Prefix'], 'ForeignKey'); $foreign_key = is_array($foreign_key) ? $foreign_key[$prefix] : $foreign_key; $changes[$key]['DependentFields'][$foreign_key] = $live_id; } } } } $this->Application->StoreVar($ses_var_name, serialize($changes)); } /** * Logs changes for a database item, tracking relevant details for create, update, or delete actions. * * @param kDBItem $object Object. * @param integer $mode Mode (e.g., create, update, delete). * @param array|null $update_fields Update fields. * * @return void */ public function logChanges(kDBItem $object, $mode, array $update_fields = null) { if ( !$mode ) { return; } $prefix = $object->Prefix; $main_prefix = $this->Application->GetTopmostPrefix($prefix); $ses_var_name = $this->getSessionVariableName($main_prefix, $prefix); $changes = $this->Application->RecallVar($ses_var_name); $changes = $changes ? unserialize($changes) : array(); $fields_hash = array( 'Prefix' => $prefix, 'ItemId' => $object->GetID(), 'OccuredOn' => adodb_mktime(), 'MasterPrefix' => $main_prefix, 'Action' => $mode, ); if ( $prefix == $main_prefix ) { // Main item. $fields_hash['MasterId'] = $object->GetID(); $fields_hash['ParentPrefix'] = array($main_prefix); $fields_hash['ParentId'] = array($main_prefix => $object->GetID()); } else { /* * Subitem. * Collect foreign key values (for serial reset). */ $foreign_keys = $this->Application->getUnitOption($prefix, 'ForeignKey', array()); $dependent_fields = $fields_hash['ParentId'] = $fields_hash['ParentPrefix'] = array(); /** @var Array $foreign_keys */ if ( is_array($foreign_keys) ) { foreach ( $foreign_keys as $foreign_key_prefix => $foreign_key_field_name ) { $dependent_fields[$foreign_key_field_name] = $object->GetDBField($foreign_key_field_name); $fields_hash['ParentPrefix'][] = $foreign_key_prefix; $fields_hash['ParentId'][$foreign_key_prefix] = $object->getParentId($foreign_key_prefix); } } else { $dependent_fields[$foreign_keys] = $object->GetDBField($foreign_keys); $fields_hash['ParentPrefix'] = array( $this->Application->getUnitOption($prefix, 'ParentPrefix'), ); $fields_hash['ParentId'][$fields_hash['ParentPrefix'][0]] = $object->getParentId('auto'); } $fields_hash['DependentFields'] = $dependent_fields; // Works only when the main item is present in url when a subitem is changed. $master_id = $this->Application->GetVar($main_prefix . '_id'); if ( $master_id === false ) { // Works in case when we're not editing topmost item, when subitem is created/updated/deleted. $master_id = $object->getParentId('auto', true); } $fields_hash['MasterId'] = $master_id; } switch ( $mode ) { case ChangeLog::UPDATE: $changed_fields = $object->GetChangedFields(); if ( $update_fields ) { $changed_fields = array_intersect_key( $changed_fields, array_combine($update_fields, $update_fields) ); } $to_save = array_merge($object->GetTitleField(), $changed_fields); break; case ChangeLog::CREATE: $to_save = $object->GetTitleField(); break; case ChangeLog::DELETE: $to_save = array_merge($object->GetTitleField(), $object->GetRealFields()); break; default: $to_save = array(); break; } $fields_hash['Changes'] = serialize($to_save); $changes[] = $fields_hash; $this->Application->StoreVar($ses_var_name, serialize($changes)); } /** * Saves changes made in the temporary table to log. * * @param string $prefix Prefix. * @param boolean $save Save changes. * * @return void */ public function saveLoggedChanges($prefix, $save = true) { $main_prefix = $this->Application->GetTopmostPrefix($prefix); // 1. get changes that were made. $ses_var_name = $this->getSessionVariableName($main_prefix, $prefix); $changes = $this->Application->RecallVar($ses_var_name); $changes = $changes ? unserialize($changes) : array(); $this->Application->RemoveVar($ses_var_name); if ( !$changes ) { // No changes, skip processing. return; } // TODO: 2. optimize change log records (replace multiple changes to same record with one change record). $to_increment = array(); // 3. collect serials to reset based on foreign keys foreach ( $changes as $index => $rec ) { if ( array_key_exists('DependentFields', $rec) ) { foreach ( $rec['DependentFields'] as $field_name => $field_value ) { // Will be "ci|ItemResourceId:345". $to_increment[] = $rec['Prefix'] . '|' . $field_name . ':' . $field_value; // Also reset sub-item prefix general serial. $to_increment[] = $rec['Prefix']; } unset($changes[$index]['DependentFields']); } // Remove keys that don't have corresponding columns in the "ChangeLogs" database table. unset($changes[$index]['ParentId'], $changes[$index]['ParentPrefix']); } // 4. collect serials to reset based on changed ids foreach ( $changes as $change ) { $to_increment[] = $change['MasterPrefix'] . '|' . $change['MasterId']; if ( $change['MasterPrefix'] != $change['Prefix'] ) { // Also reset sub-item prefix general serial. $to_increment[] = $change['Prefix']; // Will be "ci|ItemResourceId". $to_increment[] = $change['Prefix'] . '|' . $change['ItemId']; } } // 5. reset serials collected before $to_increment = array_unique($to_increment); $this->Application->incrementCacheSerial($prefix); foreach ( $to_increment as $to_increment_mixed ) { if ( strpos($to_increment_mixed, '|') !== false ) { list ($to_increment_prefix, $to_increment_id) = explode('|', $to_increment_mixed, 2); $this->Application->incrementCacheSerial($to_increment_prefix, $to_increment_id); } else { $this->Application->incrementCacheSerial($to_increment_mixed); } } // Save changes to a database. $session_log_id = $this->Application->RecallVar('_SessionLogId_'); if ( !$save || !$session_log_id ) { // Saving changes to database disabled OR related session log missing. return; } $add_fields = array( 'PortalUserId' => $this->Application->RecallVar('user_id'), 'SessionLogId' => $session_log_id, ); $change_log_table = $this->Application->getUnitOption('change-log', 'TableName'); foreach ( $changes as $rec ) { $this->Conn->doInsert(array_merge($rec, $add_fields), $change_log_table); } $this->Application->incrementCacheSerial('change-log'); $sql = 'UPDATE ' . $this->Application->getUnitOption('session-log', 'TableName') . ' SET AffectedItems = AffectedItems + ' . count($changes) . ' WHERE SessionLogId = ' . $session_log_id; $this->Conn->Query($sql); $this->Application->incrementCacheSerial('session-log'); } /** * Returns change log session variable name. * * @param string $main_prefix Main prefix. * @param string $prefix Prefix. * * @return string */ protected function getSessionVariableName($main_prefix, $prefix) { return $main_prefix . '_changes_' . $this->Application->GetTopmostWid($prefix); } }