Index: admin_templates/logs/change_logs/change_log_list.tpl
===================================================================
--- admin_templates/logs/change_logs/change_log_list.tpl	(revision 13152)
+++ admin_templates/logs/change_logs/change_log_list.tpl	(working copy)
@@ -3,42 +3,59 @@
 
 <!-- ToolBar -->
 <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
-<tbody>
-	<tr>
-  		<td>
-	  		<table width="100%" cellpadding="0" cellspacing="0">
-	  			<tr>
-	  				<td>
-	  					<script type="text/javascript">
+	<tbody>
+		<tr>
+	  		<td>
+		  		<table width="100%" cellpadding="0" cellspacing="0">
+		  			<tr>
+		  				<td>
+		  					<script type="text/javascript">
 								a_toolbar = new ToolBar();
 
-
-								function edit()
-								{
+								function edit() {
 								  	std_edit_item('change-log', 'logs/change_logs/change_log_edit');
 								}
 
-								a_toolbar.AddButton( new ToolBarButton('edit', '<inp2:m_phrase label="la_ToolTip_ViewDetails" escape="1"/>::<inp2:m_phrase label="la_ToolTip_Details" escape="1"/>', edit) );
+								a_toolbar.AddButton(
+									new ToolBarButton(
+										'edit',
+										'<inp2:m_phrase label="la_ToolTip_ViewDetails" escape="1"/>::<inp2:m_phrase label="la_ToolTip_Details" escape="1"/>',
+										edit
+									)
+								);
 
+								a_toolbar.AddButton(
+									new ToolBarButton(
+										'delete',
+										'<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>',
+										function() {
+											std_delete_items('change-log')
+										}
+									)
+								);
 
-					  		a_toolbar.AddButton( new ToolBarSeparator('sep1') );
+					  			a_toolbar.AddButton( new ToolBarSeparator('sep1') );
 
-
-								a_toolbar.AddButton( new ToolBarButton('view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function(id) {
+								a_toolbar.AddButton(
+									new ToolBarButton(
+										'view',
+										'<inp2:m_phrase label="la_ToolTip_View" escape="1"/>',
+										function(id) {
 											show_viewmenu(a_toolbar,'view');
 										}
-								) );
+									)
+								);
 
 								a_toolbar.Render();
 							</script>
-	  				</td>
+		  				</td>
 
-	  				<inp2:m_RenderElement name="search_main_toolbar" prefix="change-log" grid="Default"/>
-	  			</tr>
-	  		</table>
-		</td>
-	</tr>
-</tbody>
+		  				<inp2:m_RenderElement name="search_main_toolbar" prefix="change-log" grid="Default"/>
+		  			</tr>
+		  		</table>
+			</td>
+		</tr>
+	</tbody>
 </table>
 
 <style type="text/css">
@@ -73,7 +90,7 @@
 
 <inp2:m_RenderElement name="grid" PrefixSpecial="change-log" IdField="ChangeLogId" grid="Default" grid_filters="1" row_class_render_as="row_class"/>
 <script type="text/javascript">
-	Grids['change-log'].SetDependantToolbarButtons( new Array('edit') );
+	Grids['change-log'].SetDependantToolbarButtons( new Array('edit', 'delete') );
 </script>
 
 <inp2:m_include t="incs/footer"/>
\ No newline at end of file
Index: admin_templates/logs/session_logs/session_log_list.tpl
===================================================================
--- admin_templates/logs/session_logs/session_log_list.tpl	(revision 13152)
+++ admin_templates/logs/session_logs/session_log_list.tpl	(working copy)
@@ -3,35 +3,51 @@
 
 <!-- ToolBar -->
 <table class="toolbar" height="30" cellspacing="0" cellpadding="0" width="100%" border="0">
-<tbody>
-	<tr>
-  		<td>
-	  		<table width="100%" cellpadding="0" cellspacing="0">
-	  			<tr>
-	  				<td >
-	  					<script type="text/javascript">
+	<tbody>
+		<tr>
+	  		<td>
+		  		<table width="100%" cellpadding="0" cellspacing="0">
+		  			<tr>
+		  				<td>
+		  					<script type="text/javascript">
 								a_toolbar = new ToolBar();
 
-								function edit()
-								{
+								function edit() {
 
 								}
 
-								a_toolbar.AddButton( new ToolBarButton('view', '<inp2:m_phrase label="la_ToolTip_View" escape="1"/>', function(id) {
+								a_toolbar.AddButton(
+									new ToolBarButton(
+										'delete',
+										'<inp2:m_phrase label="la_ToolTip_Delete" escape="1"/>',
+										function() {
+											std_delete_items('session-log')
+										}
+									)
+								);
+
+								a_toolbar.AddButton( new ToolBarSeparator('sep1') );
+
+								a_toolbar.AddButton(
+									new ToolBarButton(
+										'view',
+										'<inp2:m_phrase label="la_ToolTip_View" escape="1"/>',
+										function(id) {
 											show_viewmenu(a_toolbar,'view');
 										}
-								) );
+									)
+								);
 
 								a_toolbar.Render();
 							</script>
-	  				</td>
+		  				</td>
 
-	  				<inp2:m_RenderElement name="search_main_toolbar" prefix="session-log" grid="Default"/>
-	  			</tr>
-	  		</table>
-		</td>
-	</tr>
-</tbody>
+		  				<inp2:m_RenderElement name="search_main_toolbar" prefix="session-log" grid="Default"/>
+		  			</tr>
+		  		</table>
+			</td>
+		</tr>
+	</tbody>
 </table>
 
 <style type="text/css">
@@ -54,13 +70,17 @@
 </inp2:m_DefineElement>
 
 <inp2:m_DefineElement name="affected_td">
-	<inp2:Field name="SessionLogId" result_to_var="sessionlog_id"/>
-	<a href="<inp2:m_t t="logs/change_logs/change_log_list" pass="m,change-log" grid_name="Default" custom_filters[change-log][Default][SessionLogId][like]="$sessionlog_id" change-log_event="OnSearch"/>"><inp2:Field name="AffectedItems"/></a>
+	<inp2:m_if check="Field" name="AffectedItems">
+		<inp2:Field name="SessionLogId" result_to_var="sessionlog_id"/>
+		<a href="<inp2:m_t t='logs/change_logs/change_log_list' pass='m,change-log' grid_name='Default' custom_filters[change-log][Default][SessionLogId][range][from]='$sessionlog_id' custom_filters[change-log][Default][SessionLogId][range][to]='' change-log_event='OnSearch'/>"><inp2:Field name="AffectedItems"/></a>
+	<inp2:m_else/>
+		<inp2:Field name="AffectedItems"/>
+	</inp2:m_if>
 </inp2:m_DefineElement>
 
 <inp2:m_RenderElement name="grid" PrefixSpecial="session-log" IdField="SessionLogId" grid="Default" grid_filters="1" row_class_render_as="row_class"/>
 <script type="text/javascript">
-	Grids['session-log'].SetDependantToolbarButtons( new Array() );
+	Grids['session-log'].SetDependantToolbarButtons( new Array('delete') );
 </script>
 
 <inp2:m_include t="incs/footer"/>
\ No newline at end of file
Index: install/upgrades.sql
===================================================================
--- install/upgrades.sql	(revision 13152)
+++ install/upgrades.sql	(working copy)
@@ -1650,3 +1650,5 @@
 
 DELETE FROM Phrase WHERE Phrase LIKE 'la_event_%';
 DELETE FROM PersistantSessionData WHERE VariableName = 'phrases_columns_.';
+
+UPDATE Category SET FormId = NULL WHERE FormId = 0;
Index: kernel/constants.php
===================================================================
--- kernel/constants.php	(revision 13152)
+++ kernel/constants.php	(working copy)
@@ -124,4 +124,8 @@
 	define('CATEGORY_TEMPLATE_INHERIT', '#inherit#');
 
 	define('REWRITE_MODE_BUILD', 1);
-	define('REWRITE_MODE_PARSE', 2);
\ No newline at end of file
+	define('REWRITE_MODE_PARSE', 2);
+
+	define('SESSION_LOG_ACTIVE', 0);
+	define('SESSION_LOG_LOGGED_OUT', 1);
+	define('SESSION_LOG_EXPIRED', 2);
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php	(revision 13159)
+++ kernel/db/db_event_handler.php	(working copy)
@@ -609,6 +609,8 @@
 		function LoadItem(&$event)
 		{
 			$object =& $event->getObject();
+			/* @var $object kDBItem */
+
 			$id = $this->getPassedID($event);
 
 			if ($object->isLoaded() && !is_array($id) && ($object->GetID() == $id)) {
@@ -1511,9 +1513,13 @@
 		{
 			$this->setTempWindowID($event);
 			$ids = $this->StoreSelectedIDs($event);
+
 			$var_name = $event->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
 			$this->Application->RemoveVar($var_name);
 
+			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
+			$this->Application->RemoveVar($changes_var_name);
+
 			$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
 			/* @var $temp kTempTablesHandler */
 
@@ -1563,10 +1569,12 @@
 						$this->StoreSelectedIDs($event, $live_ids);
 					}
 
-					$this->SaveLoggedChanges($changes_var_name);
+					$object =& $event->getObject();
+					/* @var $object kDBItem */
+
+					$this->SaveLoggedChanges($changes_var_name, $object->ShouldLogChanges());
 				}
 				else {
-					$this->Application->RemoveVar($changes_var_name);
 					$event->status = erFAIL;
 				}
 
@@ -1580,40 +1588,43 @@
 			}
 		}
 
-		function SaveLoggedChanges($changes_var_name)
+		function SaveLoggedChanges($changes_var_name, $save = true)
 		{
-			$ses_log_id = $this->Application->RecallVar('_SessionLogId_');
-			if (!$ses_log_id) {
-				return ;
-			}
-
+			// 1. get changes, that were made
 			$changes = $this->Application->RecallVar($changes_var_name);
 			$changes = $changes ? unserialize($changes) : Array ();
+			$this->Application->RemoveVar($changes_var_name);
+
 			if (!$changes) {
+				// no changes, skip processing
 				return ;
 			}
 
+			// save changes to database
+			$sesion_log_id = $this->Application->RecallVar('_SessionLogId_');
+
+			if (!$save || !$sesion_log_id) {
+				// saving changes to database disabled OR related session log missing
+				return ;
+			}
+
 			$add_fields = Array (
 				'PortalUserId' => $this->Application->RecallVar('user_id'),
-				'SessionLogId' => $ses_log_id,
+				'SessionLogId' => $sesion_log_id,
 			);
 
-			$changelog_table = $this->Application->getUnitOption('change-log', 'TableName');
-			$sessionlog_table = $this->Application->getUnitOption('session-log', 'TableName');
+			$change_log_table = $this->Application->getUnitOption('change-log', 'TableName');
 
 			foreach ($changes as $rec) {
-				$this->Conn->doInsert(array_merge($rec, $add_fields), $changelog_table);
+				$this->Conn->doInsert(array_merge($rec, $add_fields), $change_log_table);
 			}
 
-			$sql = 'UPDATE '.$sessionlog_table.'
-					SET AffectedItems = AffectedItems + '.count($changes).'
-					WHERE SessionLogId = '.$ses_log_id;
+			$sql = 'UPDATE ' . $this->Application->getUnitOption('session-log', 'TableName') . '
+					SET AffectedItems = AffectedItems + ' . count($changes) . '
+					WHERE SessionLogId = ' . $sesion_log_id;
 			$this->Conn->Query($sql);
-
-			$this->Application->RemoveVar($changes_var_name);
 		}
 
-
 		/**
 		 * Cancels edit
 		 * Removes all temp tables and clears selected ids
@@ -1628,6 +1639,9 @@
 			$this->clearSelectedIDs($event);
 			$event->redirect_params = Array('opener'=>'u');
 			$this->Application->RemoveVar($event->getPrefixSpecial().'_modified');
+
+			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
+			$this->Application->RemoveVar($changes_var_name);
 		}
 
 
@@ -1782,6 +1796,9 @@
 			$this->Application->SetVar($event->getPrefixSpecial().'_id', 0);
 			$this->Application->SetVar($event->getPrefixSpecial().'_PreCreate', 1);
 
+			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
+			$this->Application->RemoveVar($changes_var_name);
+
 			$event->redirect = false;
 		}
 
Index: kernel/db/dbitem.php
===================================================================
--- kernel/db/dbitem.php	(revision 13152)
+++ kernel/db/dbitem.php	(working copy)
@@ -924,9 +924,9 @@
 		$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) {
+			$this->setModifiedFlag(clDELETE); // will change affected rows, so get it before this line
 
-		if ($affected_rows > 0) {
 			// 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,14 +1103,17 @@
 	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());
 			}
 		}
 	}
@@ -1105,11 +1121,15 @@
 	/**
 	 * 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);
 
-		$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;
 	}
 
Index: kernel/utility/temp_handler.php
===================================================================
--- kernel/utility/temp_handler.php	(revision 13152)
+++ kernel/utility/temp_handler.php	(working copy)
@@ -621,6 +621,7 @@
 						$query = 'DELETE FROM '.$this->GetTempName($master['TableName']).'
 									WHERE '.$master['IdField'].' = 0';
 						$this->Conn->Query($query);
+
 						$this->UpdateChangeLogForeignKeys($master, $live_ids[$an_id], $an_id);
 					}
 				}
@@ -677,23 +678,33 @@
 
 	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);
+		if ($live_id == $temp_id) {
+			return ;
+		}
+
+		$prefix = $master['Prefix'];
+		$main_prefix = $this->Application->GetTopmostPrefix($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 ();
 
 		foreach ($changes as $key => $rec) {
-			if ($rec['Prefix'] == $master['Prefix']) {
-				if ($rec['ItemId'] == $temp_id) {
-					$changes[$key]['ItemId'] = $live_id;
-				}
+			if ($rec['Prefix'] == $prefix && $rec['ItemId'] == $temp_id) {
+				// main item change log record
+				$changes[$key]['ItemId'] = $live_id;
 			}
-			if ($rec['MasterPrefix'] == $master['Prefix']) {
-				if ($rec['MasterId'] == $temp_id) {
-					$changes[$key]['MasterId'] = $live_id;
-				}
+
+			if ($rec['MasterPrefix'] == $prefix && $rec['MasterId'] == $temp_id) {
+				// sub item change log record
+				$changes[$key]['MasterId'] = $live_id;
 			}
+
+			if (in_array($prefix, $rec['ParentPrefix']) && $rec['ParentId'][$prefix] == $temp_id) {
+				// parent item change log record
+				$changes[$key]['ParentId'][$prefix] = $live_id;
+			}
 		}
+
 		$this->Application->StoreVar($ses_var_name, serialize($changes));
 	}
 
Index: units/logs/change_logs/change_log_eh.php
===================================================================
--- units/logs/change_logs/change_log_eh.php	(revision 0)
+++ units/logs/change_logs/change_log_eh.php	(revision 0)
@@ -0,0 +1,22 @@
+<?php
+
+	class ChangeLogEventHandler extends kDBEventHandler {
+
+		/**
+		 * Updates affected record count in session, when change log record is deleted
+		 *
+		 * @param kEvent $event
+		 */
+		function OnAfterItemDelete(&$event)
+		{
+			parent::OnAfterItemDelete($event);
+
+			$object =& $event->getObject();
+			/* @var $object kDBItem */
+
+			$sql = 'UPDATE ' . $this->Application->getUnitOption('session-log', 'TableName') . '
+					SET AffectedItems = AffectedItems - 1
+					WHERE SessionLogId = ' . $object->GetDBField('SessionLogId');
+			$this->Conn->Query($sql);
+		}
+	}
\ No newline at end of file
Index: units/logs/change_logs/change_logs_config.php
===================================================================
--- units/logs/change_logs/change_logs_config.php	(revision 13159)
+++ units/logs/change_logs/change_logs_config.php	(working copy)
@@ -18,7 +18,7 @@
 		'Prefix' => 'change-log',
 		'ItemClass' => Array('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
 		'ListClass' => Array('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
-		'EventHandlerClass' => Array ('class' => 'kDBEventHandler', 'file' => '', 'build_event' => 'OnBuild'),
+		'EventHandlerClass' => Array ('class' => 'ChangeLogEventHandler', 'file' => 'change_log_eh.php', 'build_event' => 'OnBuild'),
 		'TagProcessorClass' => Array ('class' => 'kDBTagProcessor', 'file' => '', 'build_event' => 'OnBuild'),
 
 		'RegisterClasses' => Array (
@@ -36,7 +36,7 @@
 		),
 
 		'IDField' => 'ChangeLogId',
-		'StatusField' => Array ('Status'),
+		//'StatusField' => Array ('Status'),
 		'TableName' => TABLE_PREFIX.'ChangeLogs',
 
 		'TitlePresets' => Array (
@@ -47,7 +47,7 @@
 
 			'change_log_list' => Array (
 				'prefixes' => Array('change-log_List'), 'format' => '!la_tab_ChangeLog!',
-				'toolbar_buttons' => Array ('edit', 'view'),
+				'toolbar_buttons' => Array ('edit', 'delete', 'view'),
 			),
 
 			'change_log_edit' => Array (
@@ -96,6 +96,8 @@
 			),
 		),
 
+		'ForceDontLogChanges' => true,
+
 		'Fields' => Array (
 		    'ChangeLogId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 		    'PortalUserId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
@@ -137,19 +139,15 @@
 					'UserLogin' => Array ('title' => 'la_col_Username', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
 					'UserFirstName' => Array ('title' => 'la_col_FirstName', 'filter_block' => 'grid_like_filter', 'width' => 120, ),
 					'UserLastName' => Array ('title' => 'la_col_LastName', 'filter_block' => 'grid_like_filter', 'width' => 120, ),
-					'SessionLogId' => Array ('title' => 'la_col_SessionLogId', 'filter_block' => 'grid_like_filter', 'width' => 120, ),
+					'SessionLogId' => Array ('title' => 'la_col_SessionLogId', 'filter_block' => 'grid_range_filter', 'width' => 120, ),
 					'Action' => Array ('title' => 'la_col_Action', 'filter_block' => 'grid_options_filter', 'width' => 120, ),
 					'OccuredOn' => Array ('title' => 'la_col_OccuredOn', 'filter_block' => 'grid_date_range_filter', 'width' => 150, ),
 					'MasterPrefix' => Array ('title' => 'la_col_MasterPrefix', 'filter_block' => 'grid_options_filter', 'width' => 120,  ),
 					'MasterId' => Array ('title' => 'la_col_MasterId', 'filter_block' => 'grid_range_filter', 'width' => 90, ),
 					'Prefix' => Array ('title' => 'la_col_ItemPrefix', 'filter_block' => 'grid_options_filter', 'width' => 120,  ),
 					'ItemId' => Array ('title' => 'la_col_ItemId', 'filter_block' => 'grid_range_filter', 'width' => 120, ),
-					'Changes' => Array ('title' => 'la_col_Changes', 'data_block' => 'grid_changes_td', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
+					'Changes' => Array ('title' => 'la_col_Changes', 'data_block' => 'grid_changes_td', 'filter_block' => 'grid_like_filter', 'format' => 'auto_cut', 'width' => 225, ),
 				),
 			),
 		),
-
-		'ConfigMapping' => Array(
-			'PerPage' => 'Perpage_ChangeLog',
-		),
 	);
\ No newline at end of file
Index: units/logs/change_logs/changes_formatter.php
===================================================================
--- units/logs/change_logs/changes_formatter.php	(revision 13152)
+++ units/logs/change_logs/changes_formatter.php	(working copy)
@@ -33,21 +33,43 @@
 				$fld_translation = $this->Application->Phrase('la_fld_'.$field);
 
 				// remove translation link (added in debug mode)
-				$fld_translation = preg_replace('/<a href="(.*?)">(.*?)<\/a>/', '\\2', $fld_translation);
+				$fld_translation = preg_replace('/<a href="(.*?)".*>(.*?)<\/a>/', '\\2', $fld_translation);
 
 				if ($fld_translation == '!'.strtoupper('la_fld_'.$field).'!') {
 					// when phrase is not translated use field name as label
 					$fld_translation = $field;
 				}
 
-				if (is_array($data) && isset($data['old']) && isset($data['new'])) {
-					$res .= "$fld_translation: {$data['old']} => {$data['new']}<br/>\n";
+				if (is_array($data)) {
+					if ($format == 'auto_cut') {
+						$data = array_map(Array (&$this, 'cutValue'), $data);
+					}
+
+					if (array_key_exists('old', $data) && array_key_exists('new', $data)) {
+						$res .= "$fld_translation: {$data['old']} => {$data['new']}<br/>\n";
+					}
+					else {
+						$res .= "$fld_translation: {$data['new']}<br/>\n";
+					}
 				}
 				else {
-					$res .= "$fld_translation: {$data['new']}<br/>\n";
+					if ($format == 'auto_cut') {
+						$data = $this->cutValue($data);
+					}
+
+					$res .= "$fld_translation: {$data}<br/>\n";
 				}
+
 			}
 			return $res;
 		}
 
+		function cutValue($data)
+		{
+			if (strlen($data) > 200) {
+				$data = substr($data, 0, 50) . ' ...';
+			}
+
+			return $data;
+		}
 	}
\ No newline at end of file
Index: units/logs/session_logs/session_log_eh.php
===================================================================
--- units/logs/session_logs/session_log_eh.php	(revision 13152)
+++ units/logs/session_logs/session_log_eh.php	(working copy)
@@ -74,4 +74,52 @@
 			$object->Update();
 		}
 
+		/**
+		 * Don't allow to delete other user's messages
+		 *
+		 * @param kEvent $event
+		 */
+		function customProcessing(&$event, $type)
+		{
+			if ($event->Name == 'OnMassDelete' && $type == 'before') {
+				$ids = $event->getEventParam('ids');
+				if ($ids) {
+					$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
+					$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
+
+					$sql = 'SELECT ' . $id_field . '
+							FROM ' . $table_name . '
+							WHERE ' . $id_field . ' IN (' . implode(',', $ids) . ') AND Status <> ' . SESSION_LOG_ACTIVE;
+					$allowed_ids = $this->Conn->GetCol($sql);
+
+					$event->setEventParam('ids', $allowed_ids);
+				}
+			}
+		}
+
+		/**
+		 * Delete changes, related to deleted session
+		 *
+		 * @param kEvent $event
+		 */
+		function OnAfterItemDelete(&$event)
+		{
+			parent::OnAfterItemDelete($event);
+
+			$object =& $event->getObject();
+			/* @var $object kDBItem */
+
+			$sql = 'SELECT ' . $this->Application->getUnitOption('change-log', 'IDField') . '
+					FROM ' . $this->Application->getUnitOption('change-log', 'TableName') . '
+					WHERE SessionLogId = ' . $object->GetID();
+			$related_ids = $this->Conn->GetCol($sql);
+
+			if ($related_ids) {
+				$temp_handler =& $this->Application->recallObject('change-log_TempHandler', 'kTempTablesHandler');
+				/* @var $temp_handler kTempTablesHandler */
+
+				$temp_handler->DeleteItems('change-log', '', $related_ids);
+			}
+		}
+
 	}
\ No newline at end of file
Index: units/logs/session_logs/session_logs_config.php
===================================================================
--- units/logs/session_logs/session_logs_config.php	(revision 13159)
+++ units/logs/session_logs/session_logs_config.php	(working copy)
@@ -38,7 +38,7 @@
 
 		'TitlePresets' => Array (
 			'session_log_list' => Array ('prefixes' => Array('session-log_List'), 'format' => '!la_tab_SessionLogs!',
-			'toolbar_buttons' => Array ('view', 'dbl-click'),
+			'toolbar_buttons' => Array ('delete', 'view'),
 			),
 		),
 
@@ -83,6 +83,8 @@
 			),
 		),
 
+		'ForceDontLogChanges' => true,
+
 		'Fields' => Array (
 		    'SessionLogId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
 		    'PortalUserId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
Index: units/permissions/permissions_event_handler.php
===================================================================
--- units/permissions/permissions_event_handler.php	(revision 13152)
+++ units/permissions/permissions_event_handler.php	(working copy)
@@ -39,6 +39,7 @@
 		$group_id = $this->Application->GetVar('current_group_id');
 		$category_id = $this->Application->GetVar('c_id');
 		$permissions = $this->Application->GetVar($event->getPrefixSpecial(true));
+
 		if (isset($permissions[$group_id])) {
 			$permissions = $permissions[$group_id];
 
@@ -72,9 +73,9 @@
 					// not inherited
 					if (($perm_id != 0) && ($perm_value != $permissions_helper->getPermissionValue($perm_name))) {
 						// record was found in db & new value differs from old one => UPDATE
-						$update_sql[] = '	UPDATE '.$object->TableName.'
-											SET PermissionValue = '.$perm_value.'
-											WHERE (PermissionId = '.$perm_id.')';
+						$update_sql[$perm_id] = '	UPDATE '.$object->TableName.'
+													SET PermissionValue = '.$perm_value.'
+													WHERE (PermissionId = '.$perm_id.')';
 					}
 
 					if ($perm_id == 0) {
@@ -177,8 +178,12 @@
 	function UpdatePermissions(&$event, $create_sql, $update_sql, $delete_ids)
 	{
 		$object =& $event->getObject();
+		/* @var $object kDBItem */
 
 		if ($delete_ids) {
+			$action = clDELETE;
+			$object->Load($delete_ids[count($delete_ids) - 1]);
+
 			$delete_sql = '	DELETE FROM '.$object->TableName.'
 							WHERE '.$object->IDField.' IN ('.implode(',', $delete_ids).')';
 			$this->Conn->Query($delete_sql);
@@ -188,16 +193,27 @@
 			$create_sql = '	INSERT INTO '.$object->TableName.'
 							VALUES '.implode(',', $create_sql);
 			$this->Conn->Query($create_sql);
+
+			$sql = 'SELECT MIN(' . $object->IDField . ')
+					FROM ' . $object->TableName;
+			$id = $this->Conn->GetOne($sql);
+
+			$action = clCREATE;
+			$object->Load($id);
 		}
 
 		if ($update_sql) {
-			foreach ($update_sql as $sql) {
+			foreach ($update_sql as $id => $sql) {
 				$this->Conn->Query($sql);
 			}
+
+			$action = clUPDATE;
+			$object->Load($id);
+			$object->SetDBField('PermissionValue', $object->GetDBField('PermissionValue') ? 0 : 1);
 		}
 
 		if ($delete_ids || $create_sql || $update_sql) {
-			$object->setModifiedFlag();
+			$object->setModifiedFlag($action);
 
 			if ($event->Name == 'OnCategorySavePermissions') {
 				$this->Application->StoreVar('PermCache_UpdateRequired', 1);
