Index: branches/5.2.x/LICENSE
===================================================================
--- branches/5.2.x/LICENSE	(revision 16413)
+++ branches/5.2.x/LICENSE	(revision 16414)

Property changes on: branches/5.2.x/LICENSE
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/LICENSE:r15987
Index: branches/5.2.x/robots.txt
===================================================================
--- branches/5.2.x/robots.txt	(revision 16413)
+++ branches/5.2.x/robots.txt	(revision 16414)

Property changes on: branches/5.2.x/robots.txt
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/robots.txt:r15987
Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
--- branches/5.2.x/core/kernel/db/db_event_handler.php	(revision 16413)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php	(revision 16414)
@@ -1,3586 +1,3576 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	define('EH_CUSTOM_PROCESSING_BEFORE',1);
 	define('EH_CUSTOM_PROCESSING_AFTER',2);
 
 	/**
 	 * Note:
 	 *   1. When addressing variables from submit containing
 	 *	 	Prefix_Special as part of their name use
 	 *	 	$event->getPrefixSpecial(true) instead of
 	 *	 	$event->getPrefixSpecial() as usual. This is due PHP
 	 *	 	is converting "." symbols in variable names during
 	 *	 	submit info "_". $event->getPrefixSpecial optional
 	 *	 	1st parameter returns correct current Prefix_Special
 	 *	 	for variables being submitted such way (e.g. variable
 	 *	 	name that will be converted by PHP: "users.read_only_id"
 	 *	 	will be submitted as "users_read_only_id".
 	 *
 	 *	 2.	When using $this->Application-LinkVar on variables submitted
 	 *		from form which contain $Prefix_Special then note 1st item. Example:
 	 *		LinkVar($event->getPrefixSpecial(true).'_varname',$event->getPrefixSpecial().'_varname')
 	 *
 	 */
 
 	/**
 	 * EventHandler that is used to process
 	 * any database related events
 	 *
 	 */
 	class kDBEventHandler extends kEventHandler {
 
 		/**
 		 * Checks permissions of user
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(kEvent $event)
 		{
 			$section = $event->getSection();
 
 			if ( !$this->Application->isAdmin ) {
 				$allow_events = Array ('OnSearch', 'OnSearchReset', 'OnNew');
 				if ( in_array($event->Name, $allow_events) ) {
 					// allow search on front
 					return true;
 				}
 			}
 			elseif ( ($event->Name == 'OnPreSaveAndChangeLanguage') && !$this->UseTempTables($event) ) {
 				// allow changing language in grids, when not in editing mode
 				return $this->Application->CheckPermission($section . '.view', 1);
 			}
 
 			if ( !preg_match('/^CATEGORY:(.*)/', $section) ) {
 				// only if not category item events
 				if ( (substr($event->Name, 0, 9) == 'OnPreSave') || ($event->Name == 'OnSave') ) {
 					if ( $this->isNewItemCreate($event) ) {
 						return $this->Application->CheckPermission($section . '.add', 1);
 					}
 					else {
 						return $this->Application->CheckPermission($section . '.add', 1) || $this->Application->CheckPermission($section . '.edit', 1);
 					}
 				}
 			}
 
 			if ( $event->Name == 'OnPreCreate' ) {
 				// save category_id before item create (for item category selector not to destroy permission checking category)
 				$this->Application->LinkVar('m_cat_id');
 			}
 
-			if ( $event->Name == 'OnSaveWidths' ) {
-				return $this->Application->isAdminUser;
-			}
-
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Allows to override standard permission mapping
 		 *
 		 * @return void
 		 * @access protected
 		 * @see kEventHandler::$permMapping
 		 */
 		protected function mapPermissions()
 		{
 			parent::mapPermissions();
 
 			$permissions = Array (
 				'OnLoad' => Array ('self' => 'view', 'subitem' => 'view'),
 				'OnItemBuild' => Array ('self' => 'view', 'subitem' => 'view'),
-				'OnSuggestValues' => Array ('self' => 'view', 'subitem' => 'view'),
+				'OnSuggestValues' => Array ('self' => 'admin', 'subitem' => 'admin'),
 
 				'OnBuild' => Array ('self' => true),
 
 				'OnNew' => Array ('self' => 'add', 'subitem' => 'add|edit'),
 				'OnCreate' => Array ('self' => 'add', 'subitem' => 'add|edit'),
 				'OnUpdate' => Array ('self' => 'edit', 'subitem' => 'add|edit'),
 				'OnSetPrimary' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
 				'OnDelete' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
 				'OnDeleteAll' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
 				'OnMassDelete' => Array ('self' => 'delete', 'subitem' => 'add|edit'),
 				'OnMassClone' => Array ('self' => 'add', 'subitem' => 'add|edit'),
 
 				'OnCut' => Array ('self'=>'edit', 'subitem' => 'edit'),
 				'OnCopy' => Array ('self'=>'edit', 'subitem' => 'edit'),
 				'OnPaste' => Array ('self'=>'edit', 'subitem' => 'edit'),
 
 				'OnSelectItems' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
 				'OnProcessSelected' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
 				'OnStoreSelected' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
 				'OnSelectUser' => Array ('self' => 'add|edit', 'subitem' => 'add|edit'),
 
 				'OnMassApprove' => Array ('self' => 'advanced:approve|edit', 'subitem' => 'advanced:approve|add|edit'),
 				'OnMassDecline' => Array ('self' => 'advanced:decline|edit', 'subitem' => 'advanced:decline|add|edit'),
 				'OnMassMoveUp' => Array ('self' => 'advanced:move_up|edit', 'subitem' => 'advanced:move_up|add|edit'),
 				'OnMassMoveDown' => Array ('self' => 'advanced:move_down|edit', 'subitem' => 'advanced:move_down|add|edit'),
 
 				'OnPreCreate' => Array ('self' => 'add|add.pending', 'subitem' => 'edit|edit.pending'),
 				'OnEdit' => Array ('self' => 'edit|edit.pending', 'subitem' => 'edit|edit.pending'),
 
 				'OnExport' => Array ('self' => 'view|advanced:export'),
 				'OnExportBegin' => Array ('self' => 'view|advanced:export'),
 				'OnExportProgress' => Array ('self' => 'view|advanced:export'),
 
 				'OnSetAutoRefreshInterval' => Array ('self' => true, 'subitem' => true),
 				'OnAutoRefreshToggle' => Array ('self' => true, 'subitem' => true),
 
 				// theese event do not harm, but just in case check them too :)
 				'OnCancelEdit' => Array ('self' => true, 'subitem' => true),
 				'OnCancel' => Array ('self' => true, 'subitem' => true),
 				'OnReset' => Array ('self' => true, 'subitem' => true),
 
 				'OnSetSorting' => Array ('self' => true, 'subitem' => true),
 				'OnSetSortingDirect' => Array ('self' => true, 'subitem' => true),
 				'OnResetSorting' => Array ('self' => true, 'subitem' => true),
 
 				'OnSetFilter' => Array ('self' => true, 'subitem' => true),
 				'OnApplyFilters' => Array ('self' => true, 'subitem' => true),
 				'OnRemoveFilters' => Array ('self' => true, 'subitem' => true),
 				'OnSetFilterPattern' => Array ('self' => true, 'subitem' => true),
 
 				'OnSetPerPage' => Array ('self' => true, 'subitem' => true),
 				'OnSetPage' => Array ('self' => true, 'subitem' => true),
 
 				'OnSearch' => Array ('self' => true, 'subitem' => true),
 				'OnSearchReset' => Array ('self' => true, 'subitem' => true),
 
 				'OnGoBack' => Array ('self' => true, 'subitem' => true),
 
 				// it checks permission itself since flash uploader does not send cookies
 				'OnUploadFile' => Array ('self' => true, 'subitem' => true),
 				'OnDeleteFile' => Array ('self' => true, 'subitem' => true),
 
 				'OnViewFile' => Array ('self' => true, 'subitem' => true),
-				'OnSaveWidths' => Array ('self' => true, 'subitem' => true),
+				'OnSaveWidths' => Array ('self' => 'admin', 'subitem' => 'admin'),
 
 				'OnValidateMInputFields' => Array ('self' => 'view'),
 				'OnValidateField' => Array ('self' => true, 'subitem' => true),
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Define alternative event processing method names
 		 *
 		 * @return void
 		 * @see kEventHandler::$eventMethods
 		 * @access protected
 		 */
 		protected function mapEvents()
 		{
 			$events_map = Array (
 				'OnRemoveFilters' => 'FilterAction',
 				'OnApplyFilters' => 'FilterAction',
 				'OnMassApprove' => 'iterateItems',
 				'OnMassDecline' => 'iterateItems',
 				'OnMassMoveUp' => 'iterateItems',
 				'OnMassMoveDown' => 'iterateItems',
 			);
 
 			$this->eventMethods = array_merge($this->eventMethods, $events_map);
 		}
 
 		/**
 		 * Returns ID of current item to be edited
 		 * by checking ID passed in get/post as prefix_id
 		 * or by looking at first from selected ids, stored.
 		 * Returned id is also stored in Session in case
 		 * it was explicitly passed as get/post
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 * @access public
 		 */
 		public function getPassedID(kEvent $event)
 		{
 			if ( $event->getEventParam('raise_warnings') === false ) {
 				$event->setEventParam('raise_warnings', 1);
 			}
 
 			if ( $event->Special == 'previous' || $event->Special == 'next' ) {
 				$object = $this->Application->recallObject($event->getEventParam('item'));
 				/* @var $object kDBItem */
 
 				$list_helper = $this->Application->recallObject('ListHelper');
 				/* @var $list_helper ListHelper */
 
 				$select_clause = $this->Application->getUnitOption($object->Prefix, 'NavigationSelectClause', NULL);
 
 				return $list_helper->getNavigationResource($object, $event->getEventParam('list'), $event->Special == 'next', $select_clause);
 			}
 			elseif ( $event->Special == 'filter' ) {
 				// temporary object, used to print filter options only
 				return 0;
 			}
 
 			if ( preg_match('/^auto-(.*)/', $event->Special, $regs) && $this->Application->prefixRegistred($regs[1]) ) {
 				// <inp2:lang.auto-phrase_Field name="DateFormat"/> - returns field DateFormat value from language (LanguageId is extracted from current phrase object)
 				$main_object = $this->Application->recallObject($regs[1]);
 				/* @var $main_object kDBItem */
 
 				$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 				return $main_object->GetDBField($id_field);
 			}
 
 			// 1. get id from post (used in admin)
 			$ret = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id');
 			if ( ($ret !== false) && ($ret != '') ) {
 				$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
 
 				return $ret;
 			}
 
 			// 2. get id from env (used in front)
 			$ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
 			if ( ($ret !== false) && ($ret != '') ) {
 				$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
 
 				return $ret;
 			}
 
 			// recall selected ids array and use the first one
 			$ids = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids');
 			if ( $ids != '' ) {
 				$ids = explode(',', $ids);
 				if ( $ids ) {
 					$ret = array_shift($ids);
 					$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
 				}
 			}
 			else { // if selected ids are not yet stored
 				$this->StoreSelectedIDs($event);
 
 				// StoreSelectedIDs sets this variable.
 				$ret = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
 
 				if ( ($ret !== false) && ($ret != '') ) {
 					$event->setEventParam(kEvent::FLAG_ID_FROM_REQUEST, true);
 
 					return $ret;
 				}
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Prepares and stores selected_ids string
 		 * in Session and Application Variables
 		 * by getting all checked ids from grid plus
 		 * id passed in get/post as prefix_id
 		 *
 		 * @param kEvent $event
 		 * @param Array $direct_ids
 		 * @return Array
 		 * @access protected
 		 */
 		protected function StoreSelectedIDs(kEvent $event, $direct_ids = NULL)
 		{
 			$wid = $this->Application->GetTopmostWid($event->Prefix);
 			$session_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_');
 
 			$ids = $event->getEventParam('ids');
 			if ( isset($direct_ids) || ($ids !== false) ) {
 				// save ids directly if they given + reset array indexes
 				$resulting_ids = $direct_ids ? array_values($direct_ids) : ($ids ? array_values($ids) : false);
 				if ( $resulting_ids ) {
 					$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $resulting_ids));
 					$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', true);
 					$this->Application->SetVar($event->getPrefixSpecial() . '_id', $resulting_ids[0]);
 
 					return $resulting_ids;
 				}
 
 				return Array ();
 			}
 
 			$ret = Array ();
 
 			// May be we don't need this part: ?
 			$passed = $this->Application->GetVar($event->getPrefixSpecial(true) . '_id');
 			if ( $passed !== false && $passed != '' ) {
 				array_push($ret, $passed);
 			}
 
 			$ids = Array ();
 
 			// get selected ids from post & save them to session
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 			if ( $items_info ) {
 				$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
 				foreach ($items_info as $id => $field_values) {
 					if ( getArrayValue($field_values, $id_field) ) {
 						array_push($ids, $id);
 					}
 				}
 				//$ids = array_keys($items_info);
 			}
 
 			$ret = array_unique(array_merge($ret, $ids));
 
 			$this->Application->SetVar($event->getPrefixSpecial() . '_selected_ids', implode(',', $ret));
 			$this->Application->LinkVar($event->getPrefixSpecial() . '_selected_ids', $session_name, '', !$ret); // optional when IDs are missing
 
 			// This is critical - otherwise getPassedID will return last ID stored in session! (not exactly true)
 			// this smells... needs to be refactored
 			$first_id = getArrayValue($ret, 0);
 			if ( ($first_id === false) && ($event->getEventParam('raise_warnings') == 1) ) {
 				if ( $this->Application->isDebugMode() ) {
 					$this->Application->Debugger->appendTrace();
 				}
 
 				trigger_error('Requested ID for prefix <strong>' . $event->getPrefixSpecial() . '</strong> <span class="debug_error">not passed</span>', E_USER_NOTICE);
 			}
 
 			$this->Application->SetVar($event->getPrefixSpecial() . '_id', $first_id);
 			return $ret;
 		}
 
 		/**
 		 * Returns stored selected ids as an array
 		 *
 		 * @param kEvent $event
 		 * @param bool $from_session return ids from session (written, when editing was started)
 		 * @return Array
 		 * @access protected
 		 */
 		protected function getSelectedIDs(kEvent $event, $from_session = false)
 		{
 			if ( $from_session ) {
 				$wid = $this->Application->GetTopmostWid($event->Prefix);
 				$var_name = rtrim($event->getPrefixSpecial() . '_selected_ids_' . $wid, '_');
 				$ret = $this->Application->RecallVar($var_name);
 			}
 			else {
 				$ret = $this->Application->GetVar($event->getPrefixSpecial() . '_selected_ids');
 			}
 
 			return explode(',', $ret);
 		}
 
 		/**
 		 * Stores IDs, selected in grid in session
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnStoreSelected(kEvent $event)
 		{
 			$this->StoreSelectedIDs($event);
 
 			$id = $this->Application->GetVar($event->getPrefixSpecial() . '_id');
 
 			if ( $id !== false ) {
 				$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
 				$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
 			}
 		}
 
 		/**
 		 * Returns associative array of submitted fields for current item
 		 * Could be used while creating/editing single item -
 		 * meaning on any edit form, except grid edit
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 * @access protected
 		 */
 		protected function getSubmittedFields(kEvent $event)
 		{
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 			$field_values = $items_info ? array_shift($items_info) : Array ();
 
 			return $field_values;
 		}
 
 		/**
 		 * Removes any information about current/selected ids
 		 * from Application variables and Session
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function clearSelectedIDs(kEvent $event)
 		{
 			$prefix_special = $event->getPrefixSpecial();
 
 			$ids = implode(',', $this->getSelectedIDs($event, true));
 			$event->setEventParam('ids', $ids);
 
 			$wid = $this->Application->GetTopmostWid($event->Prefix);
 			$session_name = rtrim($prefix_special . '_selected_ids_' . $wid, '_');
 
 			$this->Application->RemoveVar($session_name);
 			$this->Application->SetVar($prefix_special . '_selected_ids', '');
 
 			$this->Application->SetVar($prefix_special . '_id', ''); // $event->getPrefixSpecial(true) . '_id' too may be
 		}
 
 		/**
 		 * Common builder part for Item & List
 		 *
 		 * @param kDBBase|kDBItem|kDBList $object
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function dbBuild(&$object, kEvent $event)
 		{
 			// for permission checking inside item/list build events
 			$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 
 			if ( $event->getEventParam('form_name') !== false ) {
 				$form_name = $event->getEventParam('form_name');
 			}
 			else {
 				$request_forms = $this->Application->GetVar('forms', Array ());
 				$form_name = (string)getArrayValue($request_forms, $object->getPrefixSpecial());
 			}
 
 			$object->Configure($event->getEventParam('populate_ml_fields') || $this->Application->getUnitOption($event->Prefix, 'PopulateMlFields'), $form_name);
 			$this->PrepareObject($object, $event);
 
 			$parent_event = $event->getEventParam('parent_event');
 
 			if ( is_object($parent_event) ) {
 				$object->setParentEvent($parent_event);
 			}
 
 			// force live table if specified or is original item
 			$live_table = $event->getEventParam('live_table') || $event->Special == 'original';
 
 			if ( $this->UseTempTables($event) && !$live_table ) {
 				$object->SwitchToTemp();
 			}
 
 			$this->Application->setEvent($event->getPrefixSpecial(), '');
 
 			$save_event = $this->UseTempTables($event) && $this->Application->GetTopmostPrefix($event->Prefix) == $event->Prefix ? 'OnSave' : 'OnUpdate';
 			$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', $save_event);
 		}
 
 		/**
 		 * Checks, that currently loaded item is allowed for viewing (non permission-based)
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function checkItemStatus(kEvent $event)
 		{
 			$status_fields = $this->Application->getUnitOption($event->Prefix, 'StatusField');
 			if ( !$status_fields ) {
 				return true;
 			}
 
 			$status_field = array_shift($status_fields);
 
 			if ( $status_field == 'Status' || $status_field == 'Enabled' ) {
 				$object = $event->getObject();
 				/* @var $object kDBItem */
 
 				if ( !$object->isLoaded() ) {
 					return true;
 				}
 
 				return $object->GetDBField($status_field) == STATUS_ACTIVE;
 			}
 
 			return true;
 		}
 
 		/**
 		 * Shows not found template content
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function _errorNotFound(kEvent $event)
 		{
 			if ( $event->getEventParam('raise_warnings') === 0 ) {
 				// when it's possible, that autoload fails do nothing
 				return;
 			}
 
 			if ( $this->Application->isDebugMode() ) {
 				$this->Application->Debugger->appendTrace();
 			}
 
 			trigger_error('ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>checkItemStatus</strong>, leading to "404 Not Found"', E_USER_NOTICE);
 
 			$this->Application->UrlManager->show404();
 		}
 
 		/**
 		 * Builds item (loads if needed)
 		 *
 		 * Pattern: Prototype Manager
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		protected function OnItemBuild(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$this->dbBuild($object, $event);
 
 			$sql = $this->ItemPrepareQuery($event);
 			$sql = $this->Application->ReplaceLanguageTags($sql);
 			$object->setSelectSQL($sql);
 
 			// 2. loads if allowed
 			$auto_load = $this->Application->getUnitOption($event->Prefix,'AutoLoad');
 			$skip_autoload = $event->getEventParam('skip_autoload');
 
 			if ( $auto_load && !$skip_autoload ) {
 				$perm_status = true;
 				$user_id = $this->Application->InitDone ? $this->Application->RecallVar('user_id') : USER_ROOT;
 				$event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 				$status_checked = false;
 
 				if ( $user_id == USER_ROOT || $this->CheckPermission($event) ) {
 					// don't autoload item, when user doesn't have view permission
 					$this->LoadItem($event);
 
 					$status_checked = true;
 					$editing_mode = defined('EDITING_MODE') ? EDITING_MODE : false;
 					$id_from_request = $event->getEventParam(kEvent::FLAG_ID_FROM_REQUEST);
 
 					if ( $user_id != USER_ROOT
 						&& !$this->Application->isAdmin
 						&& !($editing_mode || ($id_from_request ? $this->checkItemStatus($event) : true))
 					) {
 						// non-root user AND on front-end AND (not editing mode || incorrect status)
 						$perm_status = false;
 					}
 				}
 				else {
 					$perm_status = false;
 				}
 
 				if ( !$perm_status ) {
 					// when no permission to view item -> redirect to no permission template
 					$this->_processItemLoadingError($event, $status_checked);
 				}
 			}
 
 			$actions = $this->Application->recallObject('kActions');
 			/* @var $actions Params */
 
 			$actions->Set($event->getPrefixSpecial() . '_GoTab', '');
 			$actions->Set($event->getPrefixSpecial() . '_GoId', '');
 			$actions->Set('forms[' . $event->getPrefixSpecial() . ']', $object->getFormName());
 		}
 
 		/**
 		 * Processes case, when item wasn't loaded because of lack of permissions
 		 *
 		 * @param kEvent $event
 		 * @param bool $status_checked
 		 * @throws kNoPermissionException
 		 * @return void
 		 * @access protected
 		 */
 		protected function _processItemLoadingError($event, $status_checked)
 		{
 			$current_template = $this->Application->GetVar('t');
 			$redirect_template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
 			$error_msg = 'ItemLoad Permission Failed for prefix [' . $event->getPrefixSpecial() . '] in <strong>' . ($status_checked ? 'checkItemStatus' : 'CheckPermission') . '</strong>';
 
 			if ( $current_template == $redirect_template ) {
 				// don't perform "no_permission" redirect if already on a "no_permission" template
 				if ( $this->Application->isDebugMode() ) {
 					$this->Application->Debugger->appendTrace();
 				}
 
 				trigger_error($error_msg, E_USER_NOTICE);
 
 				return;
 			}
 
 			if ( MOD_REWRITE ) {
 				$redirect_params = Array (
 					'm_cat_id' => 0,
 					'next_template' => 'external:' . $_SERVER['REQUEST_URI'],
 				);
 			}
 			else {
 				$redirect_params = Array (
 					'next_template' => $current_template,
 				);
 			}
 
 			$exception = new kNoPermissionException($error_msg);
 			$exception->setup($redirect_template, $redirect_params);
 
 			throw $exception;
 		}
 
 		/**
 		 * Build sub-tables array from configs
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnTempHandlerBuild(kEvent $event)
 		{
 			$object = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
 			/* @var $object kTempTablesHandler */
 
 			$parent_event = $event->getEventParam('parent_event');
 			/* @var $parent_event kEvent */
 
 			if ( is_object($parent_event) ) {
 				$object->setParentEvent($parent_event);
 			}
 
 			$object->BuildTables($event->Prefix, $this->getSelectedIDs($event));
 		}
 
 		/**
 		 * Checks, that object used in event should use temp tables
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function UseTempTables(kEvent $event)
 		{
 			$top_prefix = $this->Application->GetTopmostPrefix($event->Prefix); // passed parent, not always actual
 			$special = ($top_prefix == $event->Prefix) ? $event->Special : $this->getMainSpecial($event);
 
 			return $this->Application->IsTempMode($event->Prefix, $special);
 		}
 
 		/**
 		 * Load item if id is available
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function LoadItem(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$id = $this->getPassedID($event);
 
 			if ( $object->isLoaded() && !is_array($id) && ($object->GetID() == $id) ) {
 				// object is already loaded by same id
 				return ;
 			}
 
 			if ( $object->Load($id) ) {
 				$actions = $this->Application->recallObject('kActions');
 				/* @var $actions Params */
 
 				$actions->Set($event->getPrefixSpecial() . '_id', $object->GetID());
 			}
 			else {
 				$object->setID( is_array($id) ? false : $id );
 			}
 		}
 
 		/**
 		 * Builds list
 		 *
 		 * Pattern: Prototype Manager
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		protected function OnListBuild(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			/*if ( $this->Application->isDebugMode() ) {
 				$event_params = http_build_query($event->getEventParams());
 				$this->Application->Debugger->appendHTML('InitList "<strong>' . $event->getPrefixSpecial() . '</strong>" (' . $event_params . ')');
 			}*/
 
 			$this->dbBuild($object, $event);
 
 			if ( !$object->isMainList() && $event->getEventParam('main_list') ) {
 				// once list is set to main, then even "requery" parameter can't remove that
 				/*$passed = $this->Application->GetVar('passed');
 				$this->Application->SetVar('passed', $passed . ',' . $event->Prefix);*/
 
 				$object->becameMain();
 			}
 
 			$object->setGridName($event->getEventParam('grid'));
 
 			$sql = $this->ListPrepareQuery($event);
 			$sql = $this->Application->ReplaceLanguageTags($sql);
 			$object->setSelectSQL($sql);
 
 			$object->reset();
 
 			if ( $event->getEventParam('skip_parent_filter') === false ) {
 				$object->linkToParent($this->getMainSpecial($event));
 			}
 
 			$this->AddFilters($event);
 			$this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex.
 			$this->SetPagination($event);
 			$this->SetSorting($event);
 
 			$actions = $this->Application->recallObject('kActions');
 			/* @var $actions Params */
 
 			$actions->Set('remove_specials[' . $event->getPrefixSpecial() . ']', '0');
 			$actions->Set($event->getPrefixSpecial() . '_GoTab', '');
 		}
 
 		/**
 		 * Returns special of main item for linking with sub-item
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function getMainSpecial(kEvent $event)
 		{
 			$main_special = $event->getEventParam('main_special');
 
 			if ( $main_special === false ) {
 				// main item's special not passed
 
 				if ( substr($event->Special, -5) == '-item' ) {
 					// temp handler added "-item" to given special -> process that here
 					return substr($event->Special, 0, -5);
 				}
 
 				// by default subitem's special is used for main item searching
 				return $event->Special;
 			}
 
 			return $main_special;
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Set's new per-page for grid
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetPerPage(kEvent $event)
 		{
 			$per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage');
 			$event->SetRedirectParam($event->getPrefixSpecial() . '_PerPage', $per_page);
 			$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
 
 			if ( !$this->Application->isAdminUser ) {
 				$list_helper = $this->Application->recallObject('ListHelper');
 				/* @var $list_helper ListHelper */
 
 				$this->_passListParams($event, 'per_page');
 			}
 		}
 
 		/**
 		 * Occurs when page is changed (only for hooking)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetPage(kEvent $event)
 		{
 			$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
 			$event->SetRedirectParam($event->getPrefixSpecial() . '_Page', $page);
 			$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
 
 			if ( !$this->Application->isAdminUser ) {
 				$this->_passListParams($event, 'page');
 			}
 		}
 
 		/**
 		 * Passes through main list pagination and sorting
 		 *
 		 * @param kEvent $event
 		 * @param string $skip_var
 		 * @return void
 		 * @access protected
 		 */
 		protected function _passListParams($event, $skip_var)
 		{
 			$param_names = array_diff(Array ('page', 'per_page', 'sort_by'), Array ($skip_var));
 
 			$list_helper = $this->Application->recallObject('ListHelper');
 			/* @var $list_helper ListHelper */
 
 			foreach ($param_names as $param_name) {
 				$value = $this->Application->GetVar($param_name);
 
 				switch ($param_name) {
 					case 'page':
 						if ( $value > 1 ) {
 							$event->SetRedirectParam('page', $value);
 						}
 						break;
 
 					case 'per_page':
 						if ( $value > 0 ) {
 							if ( $value != $list_helper->getDefaultPerPage($event->Prefix) ) {
 								$event->SetRedirectParam('per_page', $value);
 							}
 						}
 						break;
 
 					case 'sort_by':
 						$event->setPseudoClass('_List');
 						$object = $event->getObject(Array ('main_list' => 1));
 						/* @var $object kDBList */
 
 						if ( $list_helper->hasUserSorting($object) ) {
 							$event->SetRedirectParam('sort_by', $value);
 						}
 						break;
 				}
 			}
 		}
 
 		/**
 		 * Set's correct page for list based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetPagination(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			// get PerPage (forced -> session -> config -> 10)
 			$object->SetPerPage($this->getPerPage($event));
 
 			// main lists on Front-End have special get parameter for page
 			$page = $object->isMainList() ? $this->Application->GetVar('page') : false;
 
 			if ( !$page ) {
 				// page is given in "env" variable for given prefix
 				$page = $this->Application->GetVar($event->getPrefixSpecial() . '_Page');
 			}
 
 			if ( !$page && $event->Special ) {
 				// when not part of env, then variables like "prefix.special_Page" are
 				// replaced (by PHP) with "prefix_special_Page", so check for that too
 				$page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Page');
 			}
 
 			if ( !$object->isMainList() ) {
 				// main lists doesn't use session for page storing
 				$this->Application->StoreVarDefault($event->getPrefixSpecial() . '_Page', 1, true); // true for optional
 
 				if ( $page ) {
 					// page found in request -> store in session
 					$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', $page, true); //true for optional
 				}
 				else {
 					// page not found in request -> get from session
 					$page = $this->Application->RecallVar($event->getPrefixSpecial() . '_Page');
 				}
 
 				if ( !$event->getEventParam('skip_counting') ) {
 					// when stored page is larger, then maximal list page number
 					// (such case is also processed in kDBList::Query method)
 					$pages = $object->GetTotalPages();
 
 					if ( $page > $pages ) {
 						$page = 1;
 						$this->Application->StoreVar($event->getPrefixSpecial() . '_Page', 1, true);
 					}
 				}
 			}
 
 			$object->SetPage($page);
 		}
 
 		/**
 		 * Returns current per-page setting for list
 		 *
 		 * @param kEvent $event
 		 * @return int
 		 * @access protected
 		 */
 		protected function getPerPage(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$per_page = $event->getEventParam('per_page');
 
 			if ( $per_page ) {
 				// per-page is passed as tag parameter to PrintList, InitList, etc.
 				$config_mapping = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 
 				// 2. per-page setting is stored in configuration variable
 				if ( $config_mapping ) {
 					// such pseudo per-pages are only defined in templates directly
 					switch ($per_page) {
 						case 'short_list':
 							$per_page = $this->Application->ConfigValue($config_mapping['ShortListPerPage']);
 							break;
 
 						case 'default':
 							$per_page = $this->Application->ConfigValue($config_mapping['PerPage']);
 							break;
 					}
 				}
 
 				return $per_page;
 			}
 
 			if ( !$per_page && $object->isMainList() ) {
 				// main lists on Front-End have special get parameter for per-page
 				$per_page = $this->Application->GetVar('per_page');
 			}
 
 			if ( !$per_page ) {
 				// per-page is given in "env" variable for given prefix
 				$per_page = $this->Application->GetVar($event->getPrefixSpecial() . '_PerPage');
 			}
 
 			if ( !$per_page && $event->Special ) {
 				// when not part of env, then variables like "prefix.special_PerPage" are
 				// replaced (by PHP) with "prefix_special_PerPage", so check for that too
 				$per_page = $this->Application->GetVar($event->getPrefixSpecial(true) . '_PerPage');
 			}
 
 			if ( !$object->isMainList() ) {
 				// per-page given in env and not in main list
 				$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 
 				if ( $per_page ) {
 					// per-page found in request -> store in session and persistent session
 					$this->setListSetting($event, 'PerPage', $per_page);
 				}
 				else {
 					// per-page not found in request -> get from pesistent session (or session)
 					$per_page = $this->getListSetting($event, 'PerPage');
 				}
 			}
 
 			if ( !$per_page ) {
 				// per page wan't found in request/session/persistent session
 				$list_helper = $this->Application->recallObject('ListHelper');
 				/* @var $list_helper ListHelper */
 
 				// allow to override default per-page value from tag
 				$default_per_page = $event->getEventParam('default_per_page');
 
 				if ( !is_numeric($default_per_page) ) {
 					$default_per_page = $this->Application->ConfigValue('DefaultGridPerPage');
 				}
 
 				$per_page = $list_helper->getDefaultPerPage($event->Prefix, $default_per_page);
 			}
 
 			return $per_page;
 		}
 
 		/**
 		 * Set's correct sorting for list based on data provided with event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetSorting(kEvent $event)
 		{
 			$event->setPseudoClass('_List');
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			if ( $object->isMainList() ) {
 				$sort_by = $this->Application->GetVar('sort_by');
 				$cur_sort1 = $cur_sort1_dir = $cur_sort2 = $cur_sort2_dir = false;
 
 				if ( $sort_by ) {
 					$sortings = explode('|', $sort_by);
 					list ($cur_sort1, $cur_sort1_dir) = explode(',', $sortings[0]);
 
 					if ( isset($sortings[1]) ) {
 						list ($cur_sort2, $cur_sort2_dir) = explode(',', $sortings[1]);
 					}
 				}
 			}
 			else {
 				$sorting_settings = $this->getListSetting($event, 'Sortings');
 
 				$cur_sort1 = getArrayValue($sorting_settings, 'Sort1');
 				$cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir');
 				$cur_sort2 = getArrayValue($sorting_settings, 'Sort2');
 				$cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir');
 			}
 
 			$tag_sort_by = $event->getEventParam('sort_by');
 
 			if ( $tag_sort_by ) {
 				if ( $tag_sort_by == 'random' ) {
 					$object->AddOrderField('RAND()', '');
 				}
 				else {
 					// multiple sortings could be specified at once
 					$tag_sort_by = explode('|', $tag_sort_by);
 
 					foreach ($tag_sort_by as $sorting_element) {
 						list ($by, $dir) = explode(',', $sorting_element);
 						$object->AddOrderField($by, $dir);
 					}
 				}
 			}
 
 			$list_sortings = $this->_getDefaultSorting($event);
 
 			// use default if not specified in session
 			if ( !$cur_sort1 || !$cur_sort1_dir ) {
 				$sorting = getArrayValue($list_sortings, 'Sorting');
 
 				if ( $sorting ) {
 					reset($sorting);
 					$cur_sort1 = key($sorting);
 					$cur_sort1_dir = current($sorting);
 
 					if ( next($sorting) ) {
 						$cur_sort2 = key($sorting);
 						$cur_sort2_dir = current($sorting);
 					}
 				}
 			}
 
 			// always add forced sorting before any user sorting fields
 			$forced_sorting = getArrayValue($list_sortings, 'ForcedSorting');
 			/* @var $forced_sorting Array */
 
 			if ( $forced_sorting ) {
 				foreach ($forced_sorting as $field => $dir) {
 					$object->AddOrderField($field, $dir);
 				}
 			}
 
 			// add user sorting fields
 			if ( $cur_sort1 != '' && $cur_sort1_dir != '' ) {
 				$object->AddOrderField($cur_sort1, $cur_sort1_dir);
 			}
 
 			if ( $cur_sort2 != '' && $cur_sort2_dir != '' ) {
 				$object->AddOrderField($cur_sort2, $cur_sort2_dir);
 			}
 		}
 
 		/**
 		 * Returns default list sortings
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 * @access protected
 		 */
 		protected function _getDefaultSorting(kEvent $event)
 		{
 			$list_sortings = $this->Application->getUnitOption($event->Prefix, 'ListSortings', Array ());
 			$sorting_prefix = array_key_exists($event->Special, $list_sortings) ? $event->Special : '';
 			$sorting_configs = $this->Application->getUnitOption($event->Prefix, 'ConfigMapping');
 
 			if ( $sorting_configs && array_key_exists('DefaultSorting1Field', $sorting_configs) ) {
 				// sorting defined in configuration variables overrides one from unit config
 				$list_sortings[$sorting_prefix]['Sorting'] = Array (
 					$this->Application->ConfigValue($sorting_configs['DefaultSorting1Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting1Dir']),
 					$this->Application->ConfigValue($sorting_configs['DefaultSorting2Field']) => $this->Application->ConfigValue($sorting_configs['DefaultSorting2Dir']),
 				);
 
 				// TODO: lowercase configuration variable values in db, instead of here
 				$list_sortings[$sorting_prefix]['Sorting'] = array_map('strtolower', $list_sortings[$sorting_prefix]['Sorting']);
 			}
 
 			return isset($list_sortings[$sorting_prefix]) ? $list_sortings[$sorting_prefix] : Array ();
 		}
 
 		/**
 		 * Gets list setting by name (persistent or real session)
 		 *
 		 * @param kEvent $event
 		 * @param string $variable_name
 		 * @return string|Array
 		 * @access protected
 		 */
 		protected function getListSetting(kEvent $event, $variable_name)
 		{
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 			$storage_prefix = $event->getEventParam('same_special') ? $event->Prefix : $event->getPrefixSpecial();
 
 			// get sorting from persistent session
 			$default_value = $this->Application->isAdmin ? ALLOW_DEFAULT_SETTINGS : false;
 			$variable_value = $this->Application->RecallPersistentVar($storage_prefix . '_' . $variable_name . '.' . $view_name, $default_value);
 
 			/*if ( !$variable_value ) {
 				// get sorting from session
 				$variable_value = $this->Application->RecallVar($storage_prefix . '_' . $variable_name);
 			}*/
 
 			if ( kUtil::IsSerialized($variable_value) ) {
 				$variable_value = unserialize($variable_value);
 			}
 
 			return $variable_value;
 		}
 
 		/**
 		 * Sets list setting by name (persistent and real session)
 		 *
 		 * @param kEvent $event
 		 * @param string $variable_name
 		 * @param string|Array $variable_value
 		 * @return void
 		 * @access protected
 		 */
 		protected function setListSetting(kEvent $event, $variable_name, $variable_value = NULL)
 		{
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 //			$this->Application->StoreVar($event->getPrefixSpecial() . '_' . $variable_name, $variable_value, true); //true for optional
 
 			if ( isset($variable_value) ) {
 				if ( is_array($variable_value) ) {
 					$variable_value = serialize($variable_value);
 				}
 
 				$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name, $variable_value, true); //true for optional
 			}
 			else {
 				$this->Application->RemovePersistentVar($event->getPrefixSpecial() . '_' . $variable_name . '.' . $view_name);
 			}
 		}
 
 		/**
 		 * Add filters found in session
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function AddFilters(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$edit_mark = rtrim($this->Application->GetSID() . '_' . $this->Application->GetTopmostWid($event->Prefix), '_');
 
 			// add search filter
 			$filter_data = $this->Application->RecallVar($event->getPrefixSpecial() . '_search_filter');
 
 			if ( $filter_data ) {
 				$filter_data = unserialize($filter_data);
 
 				foreach ($filter_data as $filter_field => $filter_params) {
 					$filter_type = ($filter_params['type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER;
 					$filter_value = str_replace(EDIT_MARK, $edit_mark, $filter_params['value']);
 					$object->addFilter($filter_field, $filter_value, $filter_type, kDBList::FLT_SEARCH);
 				}
 			}
 
 			// add custom filter
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 			$custom_filters = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_custom_filter.' . $view_name);
 
 			if ( $custom_filters ) {
 				$grid_name = $event->getEventParam('grid');
 				$custom_filters = unserialize($custom_filters);
 
 				if ( isset($custom_filters[$grid_name]) ) {
 					foreach ($custom_filters[$grid_name] as $field_name => $field_options) {
 						list ($filter_type, $field_options) = each($field_options);
 
 						if ( isset($field_options['value']) && $field_options['value'] ) {
 							$filter_type = ($field_options['sql_filter_type'] == 'having') ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER;
 							$filter_value = str_replace(EDIT_MARK, $edit_mark, $field_options['value']);
 							$object->addFilter($field_name, $filter_value, $filter_type, kDBList::FLT_CUSTOM);
 						}
 					}
 				}
 			}
 
 			// add view filter
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
 
 			if ( $view_filter ) {
 				$view_filter = unserialize($view_filter);
 
 				$temp_filter = $this->Application->makeClass('kMultipleFilter');
 				/* @var $temp_filter kMultipleFilter */
 
 				$filter_menu = $this->Application->getUnitOption($event->Prefix, 'FilterMenu');
 
 				$group_key = 0;
 				$group_count = count($filter_menu['Groups']);
 
 				while ($group_key < $group_count) {
 					$group_info = $filter_menu['Groups'][$group_key];
 
 					$temp_filter->setType(constant('kDBList::FLT_TYPE_' . $group_info['mode']));
 					$temp_filter->clearFilters();
 
 					foreach ($group_info['filters'] as $flt_id) {
 						$sql_key = getArrayValue($view_filter, $flt_id) ? 'on_sql' : 'off_sql';
 
 						if ( $filter_menu['Filters'][$flt_id][$sql_key] != '' ) {
 							$temp_filter->addFilter('view_filter_' . $flt_id, $filter_menu['Filters'][$flt_id][$sql_key]);
 						}
 					}
 
 					$object->addFilter('view_group_' . $group_key, $temp_filter, $group_info['type'], kDBList::FLT_VIEW);
 					$group_key++;
 				}
 			}
 
 			// add item filter
 			if ( $object->isMainList() ) {
 				$this->applyItemFilters($event);
 			}
 		}
 
 		/**
 		 * Applies item filters
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function applyItemFilters($event)
 		{
 			$filter_values = $this->Application->GetVar('filters', Array ());
 
 			if ( !$filter_values ) {
 				return;
 			}
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$where_clause = Array (
 				'ItemPrefix = ' . $this->Conn->qstr($object->Prefix),
 				'FilterField IN (' . implode(',', $this->Conn->qstrArray(array_keys($filter_values))) . ')',
 				'Enabled = 1',
 			);
 
 			$sql = 'SELECT *
 					FROM ' . $this->Application->getUnitOption('item-filter', 'TableName') . '
 					WHERE (' . implode(') AND (', $where_clause) . ')';
 			$filters = $this->Conn->Query($sql, 'FilterField');
 
 			foreach ($filters as $filter_field => $filter_data) {
 				$filter_value = $filter_values[$filter_field];
 
 				if ( "$filter_value" === '' ) {
 					// ListManager don't pass empty values, but check here just in case
 					continue;
 				}
 
 				$table_name = $object->isVirtualField($filter_field) ? '' : '%1$s.';
 
 				switch ($filter_data['FilterType']) {
 					case 'radio':
 						$filter_value = $table_name . '`' . $filter_field . '` = ' . $this->Conn->qstr($filter_value);
 						break;
 
 					case 'checkbox':
 						$filter_value = explode('|', substr($filter_value, 1, -1));
 						$filter_value = $this->Conn->qstrArray($filter_value, 'escape');
 
 						if ( $object->GetFieldOption($filter_field, 'multiple') ) {
 							$filter_value = $table_name . '`' . $filter_field . '` LIKE "%|' . implode('|%" OR ' . $table_name . '`' . $filter_field . '` LIKE "%|', $filter_value) . '|%"';
 						}
 						else {
 							$filter_value = $table_name . '`' . $filter_field . '` IN (' . implode(',', $filter_value) . ')';
 						}
 						break;
 
 					case 'range':
 						$filter_value = $this->Conn->qstrArray(explode('-', $filter_value));
 						$filter_value = $table_name . '`' . $filter_field . '` BETWEEN ' . $filter_value[0] . ' AND ' . $filter_value[1];
 						break;
 				}
 
 				$object->addFilter('item_filter_' . $filter_field, $filter_value, $object->isVirtualField($filter_field) ? kDBList::HAVING_FILTER : kDBList::WHERE_FILTER);
 			}
 		}
 
 		/**
 		 * Set's new sorting for list
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetSorting(kEvent $event)
 		{
 			$sorting_settings = $this->getListSetting($event, 'Sortings');
 			$cur_sort1 = getArrayValue($sorting_settings, 'Sort1');
 			$cur_sort1_dir = getArrayValue($sorting_settings, 'Sort1_Dir');
 
 			$use_double_sorting = $this->Application->ConfigValue('UseDoubleSorting');
 
 			if ( $use_double_sorting ) {
 				$cur_sort2 = getArrayValue($sorting_settings, 'Sort2');
 				$cur_sort2_dir = getArrayValue($sorting_settings, 'Sort2_Dir');
 			}
 
 			$passed_sort1 = $this->Application->GetVar($event->getPrefixSpecial(true) . '_Sort1');
 			if ( $cur_sort1 == $passed_sort1 ) {
 				$cur_sort1_dir = $cur_sort1_dir == 'asc' ? 'desc' : 'asc';
 			}
 			else {
 				if ( $use_double_sorting ) {
 					$cur_sort2 = $cur_sort1;
 					$cur_sort2_dir = $cur_sort1_dir;
 				}
 
 				$cur_sort1 = $passed_sort1;
 				$cur_sort1_dir = 'asc';
 			}
 
 			$sorting_settings = Array ('Sort1' => $cur_sort1, 'Sort1_Dir' => $cur_sort1_dir);
 
 			if ( $use_double_sorting ) {
 				$sorting_settings['Sort2'] = $cur_sort2;
 				$sorting_settings['Sort2_Dir'] = $cur_sort2_dir;
 			}
 
 			$this->setListSetting($event, 'Sortings', $sorting_settings);
 		}
 
 		/**
 		 * Set sorting directly to session (used for category item sorting (front-end), grid sorting (admin, view menu)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetSortingDirect(kEvent $event)
 		{
 			// used on Front-End in category item lists
 			$prefix_special = $event->getPrefixSpecial();
 			$combined = $this->Application->GetVar($event->getPrefixSpecial(true) . '_CombinedSorting');
 
 			if ( $combined ) {
 				list ($field, $dir) = explode('|', $combined);
 
 				if ( $this->Application->isAdmin || !$this->Application->GetVar('main_list') ) {
 					$this->setListSetting($event, 'Sortings', Array ('Sort1' => $field, 'Sort1_Dir' => $dir));
 				}
 				else {
 					$event->setPseudoClass('_List');
 					$this->Application->SetVar('sort_by', $field . ',' . $dir);
 
 					$object = $event->getObject(Array ('main_list' => 1));
 					/* @var $object kDBList */
 
 					$list_helper = $this->Application->recallObject('ListHelper');
 					/* @var $list_helper ListHelper */
 
 					$this->_passListParams($event, 'sort_by');
 
 					if ( $list_helper->hasUserSorting($object) ) {
 						$event->SetRedirectParam('sort_by', $field . ',' . strtolower($dir));
 					}
 
 					$event->SetRedirectParam('pass', 'm');
 				}
 
 				return;
 			}
 
 			// used in "View Menu -> Sort" menu in administrative console
 			$field_pos = $this->Application->GetVar($event->getPrefixSpecial(true) . '_SortPos');
 			$this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos, $prefix_special . '_Sort' . $field_pos);
 			$this->Application->LinkVar($event->getPrefixSpecial(true) . '_Sort' . $field_pos . '_Dir', $prefix_special . '_Sort' . $field_pos . '_Dir');
 		}
 
 		/**
 		 * Reset grid sorting to default (from config)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnResetSorting(kEvent $event)
 		{
 			$this->setListSetting($event, 'Sortings');
 		}
 
 		/**
 		 * Sets grid refresh interval
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetAutoRefreshInterval(kEvent $event)
 		{
 			$refresh_interval = $this->Application->GetVar('refresh_interval');
 
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 			$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_interval);
 		}
 
 		/**
 		 * Changes auto-refresh state for grid
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAutoRefreshToggle(kEvent $event)
 		{
 			$refresh_intervals = $this->Application->ConfigValue('AutoRefreshIntervals');
 			if ( !$refresh_intervals ) {
 				return;
 			}
 
 			$view_name = $this->Application->RecallVar($event->getPrefixSpecial() . '_current_view');
 			$auto_refresh = $this->Application->RecallPersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name);
 
 			if ( $auto_refresh === false ) {
 				$refresh_intervals = explode(',', $refresh_intervals);
 				$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_refresh_interval.' . $view_name, $refresh_intervals[0]);
 			}
 
 			$this->Application->StorePersistentVar($event->getPrefixSpecial() . '_auto_refresh.' . $view_name, $auto_refresh ? 0 : 1);
 		}
 
 		/**
 		 * Creates needed sql query to load item,
 		 * if no query is defined in config for
 		 * special requested, then use list query
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function ItemPrepareQuery(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$sqls = $object->getFormOption('ItemSQLs', Array ());
 			$special = isset($sqls[$event->Special]) ? $event->Special : '';
 
 			// preferred special not found in ItemSQLs -> use analog from ListSQLs
 
 			return isset($sqls[$special]) ? $sqls[$special] : $this->ListPrepareQuery($event);
 		}
 
 		/**
 		 * Creates needed sql query to load list,
 		 * if no query is defined in config for
 		 * special requested, then use default
 		 * query
 		 *
 		 * @param kEvent $event
 		 * @return string
 		 * @access protected
 		 */
 		protected function ListPrepareQuery(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$sqls = $object->getFormOption('ListSQLs', Array ());
 
 			return $sqls[array_key_exists($event->Special, $sqls) ? $event->Special : ''];
 		}
 
 		/**
 		 * Apply custom processing to item
 		 *
 		 * @param kEvent $event
 		 * @param string $type
 		 * @return void
 		 * @access protected
 		 */
 		protected function customProcessing(kEvent $event, $type)
 		{
 
 		}
 
 		/* Edit Events mostly used in Admin */
 
 		/**
 		 * Creates new kDBItem
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnCreate(kEvent $event)
 		{
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			if ( !$items_info ) {
 				return;
 			}
 
 			list($id, $field_values) = each($items_info);
 			$object->setID($id);
 			$object->SetFieldsFromHash($field_values);
 			$event->setEventParam('form_data', $field_values);
 
 			$this->customProcessing($event, 'before');
 
 			// look at kDBItem' Create for ForceCreateId description, it's rarely used and is NOT set by default
 			if ( $object->Create($event->getEventParam('ForceCreateId')) ) {
 				$this->customProcessing($event, 'after');
 				$event->SetRedirectParam('opener', 'u');
 				return;
 			}
 
 			$event->redirect = false;
 			$event->status = kEvent::erFAIL;
 			$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate');
 		}
 
 		/**
 		 * Updates kDBItem
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnUpdate(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$this->_update($event);
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Updates data in database based on request
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function _update(kEvent $event)
 		{
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
 
 			if ( $items_info ) {
 				foreach ($items_info as $id => $field_values) {
 					$object->Load($id);
 					$object->SetFieldsFromHash($field_values);
 					$event->setEventParam('form_data', $field_values);
 					$this->customProcessing($event, 'before');
 
 					if ( $object->Update($id) ) {
 						$this->customProcessing($event, 'after');
 						$event->status = kEvent::erSUCCESS;
 					}
 					else {
 						$event->status = kEvent::erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 		}
 
 		/**
 		 * Delete's kDBItem object
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnDelete(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($this->getPassedID($event)));
 		}
 
 		/**
 		 * Deletes all records from table
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnDeleteAll(kEvent $event)
 		{
 			$sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName');
 			$ids = $this->Conn->GetCol($sql);
 
 			if ( $ids ) {
 				$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 				/* @var $temp_handler kTempTablesHandler */
 
 				$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
 			}
 		}
 
 		/**
 		 * Prepares new kDBItem object
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnNew(kEvent $event)
 		{
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$object->Clear(0);
 			$this->Application->SetVar($event->getPrefixSpecial() . '_SaveEvent', 'OnCreate');
 
 			if ( $event->getEventParam('top_prefix') != $event->Prefix ) {
 				// this is subitem prefix, so use main item special
 				$table_info = $object->getLinkedInfo($this->getMainSpecial($event));
 			}
 			else {
 				$table_info = $object->getLinkedInfo();
 			}
 
 			$object->SetDBField($table_info['ForeignKey'], $table_info['ParentId']);
 
 			$event->redirect = false;
 		}
 
 		/**
 		 * Cancels kDBItem Editing/Creation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnCancel(kEvent $event)
 		{
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			if ( $items_info ) {
 				$delete_ids = Array ();
 
 				$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 				/* @var $temp_handler kTempTablesHandler */
 
 				foreach ($items_info as $id => $field_values) {
 					$object->Load($id);
 					// record created for using with selector (e.g. Reviews->Select User), and not validated => Delete it
 					if ( $object->isLoaded() && !$object->Validate() && ($id <= 0) ) {
 						$delete_ids[] = $id;
 					}
 				}
 
 				if ( $delete_ids ) {
 					$temp_handler->DeleteItems($event->Prefix, $event->Special, $delete_ids);
 				}
 			}
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Deletes all selected items.
 		 * Automatically recurse into sub-items using temp handler, and deletes sub-items
 		 * by calling its Delete method if sub-item has AutoDelete set to true in its config file
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnMassDelete(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return ;
 			}
 
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			$event->setEventParam('ids', $ids);
 			$this->customProcessing($event, 'before');
 			$ids = $event->getEventParam('ids');
 
 			if ( $ids ) {
 				$temp_handler->DeleteItems($event->Prefix, $event->Special, $ids);
 			}
 
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Sets window id (of first opened edit window) to temp mark in uls
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function setTempWindowID(kEvent $event)
 		{
 			$prefixes = Array ($event->Prefix, $event->getPrefixSpecial(true));
 
 			foreach ($prefixes as $prefix) {
 				$mode = $this->Application->GetVar($prefix . '_mode');
 
 				if ($mode == 't') {
 					$wid = $this->Application->GetVar('m_wid');
 					$this->Application->SetVar(str_replace('_', '.', $prefix) . '_mode', 't' . $wid);
 					break;
 				}
 			}
 		}
 
 		/**
 		 * Prepare temp tables and populate it
 		 * with items selected in the grid
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnEdit(kEvent $event)
 		{
 			$this->setTempWindowID($event);
 			$ids = $this->StoreSelectedIDs($event);
 
 			$object = $event->getObject(Array('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$object->setPendingActions(null, true);
 
 			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
 			$this->Application->RemoveVar($changes_var_name);
 
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$temp_handler->PrepareEdit();
 
 			$event->SetRedirectParam('m_lang', $this->Application->GetDefaultLanguageId());
 			$event->SetRedirectParam($event->getPrefixSpecial() . '_id', array_shift($ids));
 			$event->SetRedirectParam('pass', 'all,' . $event->getPrefixSpecial());
 
 			$simultaneous_edit_message = $this->Application->GetVar('_simultaneous_edit_message');
 
 			if ( $simultaneous_edit_message ) {
 				$event->SetRedirectParam('_simultaneous_edit_message', $simultaneous_edit_message);
 			}
 		}
 
 		/**
 		 * Saves content of temp table into live and
 		 * redirects to event' default redirect (normally grid template)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSave(kEvent $event)
 		{
 			$event->CallSubEvent('OnPreSave');
 
 			if ( $event->status != kEvent::erSUCCESS ) {
 				return;
 			}
 
 			$skip_master = false;
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
 
 			if ( !$this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$live_ids = $temp_handler->SaveEdit($event->getEventParam('master_ids') ? $event->getEventParam('master_ids') : Array ());
 
 				if ( $live_ids === false ) {
 					// coping from table failed, because we have another coping process to same table, that wasn't finished
 					$event->status = kEvent::erFAIL;
 					return;
 				}
 
 				if ( $live_ids ) {
 					// ensure, that newly created item ids are available as if they were selected from grid
 					// NOTE: only works if main item has sub-items !!!
 					$this->StoreSelectedIDs($event, $live_ids);
 				}
 
 				$object = $event->getObject();
 				/* @var $object kDBItem */
 
 				$this->SaveLoggedChanges($changes_var_name, $object->ShouldLogChanges());
 			}
 			else {
 				$event->status = kEvent::erFAIL;
 			}
 
 			$this->clearSelectedIDs($event);
 
 			$event->SetRedirectParam('opener', 'u');
 			$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
 
 			// all temp tables are deleted here => all after hooks should think, that it's live mode now
 			$this->Application->SetVar($event->Prefix . '_mode', '');
 		}
 
 		/**
 		 * Saves changes made in temporary table to log
 		 *
 		 * @param string $changes_var_name
 		 * @param bool $save
 		 * @return void
 		 * @access public
 		 */
 		public function SaveLoggedChanges($changes_var_name, $save = true)
 		{
 			// 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 ;
 			}
 
 			// 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']);
 				}
 
 				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($this->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 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' => $sesion_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 = ' . $sesion_log_id;
 			$this->Conn->Query($sql);
 
 			$this->Application->incrementCacheSerial('session-log');
 		}
 
 		/**
 		 * Cancels edit
 		 * Removes all temp tables and clears selected ids
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnCancelEdit(kEvent $event)
 		{
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$temp_handler->CancelEdit();
 			$this->clearSelectedIDs($event);
 
 			$this->Application->RemoveVar($event->getPrefixSpecial() . '_modified');
 
 			$changes_var_name = $this->Prefix . '_changes_' . $this->Application->GetTopmostWid($this->Prefix);
 			$this->Application->RemoveVar($changes_var_name);
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Allows to determine if we are creating new item or editing already created item
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function isNewItemCreate(kEvent $event)
 		{
 			$object = $event->getObject( Array ('raise_warnings' => 0) );
 			/* @var $object kDBItem */
 
 			return !$object->isLoaded();
 		}
 
 		/**
 		 * Saves edited item into temp table
 		 * If there is no id, new item is created in temp table
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSave(kEvent $event)
 		{
 			// if there is no id - it means we need to create an item
 			if ( is_object($event->MasterEvent) ) {
 				$event->MasterEvent->setEventParam('IsNew', false);
 			}
 
 			if ( $this->isNewItemCreate($event) ) {
 				$event->CallSubEvent('OnPreSaveCreated');
 
 				if ( is_object($event->MasterEvent) ) {
 					$event->MasterEvent->setEventParam('IsNew', true);
 				}
 
 				return ;
 			}
 
 			// don't just call OnUpdate event here, since it maybe overwritten to Front-End specific behavior
 			$this->_update($event);
 		}
 
 		/**
 		 * Analog of OnPreSave event for usage in AJAX request
 		 *
 		 * @param kEvent $event
 		 *
 		 * @return void
 		 */
 		protected function OnPreSaveAjax(kEvent $event)
 		{
 			$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
 			/* @var $ajax_form_helper AjaxFormHelper */
 
 			$ajax_form_helper->transitEvent($event, 'OnPreSave');
 		}
 
 		/**
 		 * [HOOK] Saves sub-item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveSubItem(kEvent $event)
 		{
 			$not_created = $this->isNewItemCreate($event);
 
 			$event->CallSubEvent($not_created ? 'OnCreate' : 'OnUpdate');
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$object = $event->getObject();
 				/* @var $object kDBItem */
 
 				$this->Application->SetVar($event->getPrefixSpecial() . '_id', $object->GetID());
 			}
 			else {
 				$event->MasterEvent->status = $event->status;
 			}
 
 			$event->SetRedirectParam('opener', 's');
 		}
 
 		/**
 		 * Saves edited item in temp table and loads
 		 * item with passed id in current template
 		 * Used in Prev/Next buttons
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveAndGo(kEvent $event)
 		{
 			$event->CallSubEvent('OnPreSave');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$id = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId');
 				$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $id);
 			}
 		}
 
 		/**
 		 * Saves edited item in temp table and goes
 		 * to passed tabs, by redirecting to it with OnPreSave event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveAndGoToTab(kEvent $event)
 		{
 			$event->CallSubEvent('OnPreSave');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab');
 			}
 		}
 
 		/**
 		 * Saves editable list and goes to passed tab,
 		 * by redirecting to it with empty event
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnUpdateAndGoToTab(kEvent $event)
 		{
 			$event->setPseudoClass('_List');
 			$event->CallSubEvent('OnUpdate');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$event->redirect = $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoTab');
 			}
 		}
 
 		/**
 		 * Prepare temp tables for creating new item
 		 * but does not create it. Actual create is
 		 * done in OnPreSaveCreated
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreCreate(kEvent $event)
 		{
 			$this->setTempWindowID($event);
 			$this->clearSelectedIDs($event);
 			$this->Application->SetVar('m_lang', $this->Application->GetDefaultLanguageId());
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$temp_handler = $this->Application->recallObject($event->Prefix . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$temp_handler->PrepareEdit();
 
 			$object->setID(0);
 			$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;
 		}
 
 		/**
 		 * Creates a new item in temp table and
 		 * stores item id in App vars and Session on success
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveCreated(kEvent $event)
 		{
 			$object = $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			$object->setID(0);
 			$field_values = $this->getSubmittedFields($event);
 			$object->SetFieldsFromHash($field_values);
 			$event->setEventParam('form_data', $field_values);
 			$this->customProcessing($event, 'before');
 
 			if ( $object->Create() ) {
 				$this->customProcessing($event, 'after');
 				$event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID());
 			}
 			else {
 				$event->status = kEvent::erFAIL;
 				$event->redirect = false;
 			}
 		}
 
 		/**
 		 * Reloads form to loose all changes made during item editing
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnReset(kEvent $event)
 		{
 			//do nothing - should reset :)
 			if ( $this->isNewItemCreate($event) ) {
 				// just reset id to 0 in case it was create
 				$object = $event->getObject( Array ('skip_autoload' => true) );
 				/* @var $object kDBItem */
 
 				$object->setID(0);
 				$this->Application->SetVar($event->getPrefixSpecial() . '_id', 0);
 			}
 		}
 
 		/**
 		 * Apply same processing to each item being selected in grid
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function iterateItems(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return ;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			if ( $ids ) {
 				$status_field = $object->getStatusField();
 				$order_field = $this->Application->getUnitOption($event->Prefix, 'OrderField');
 
 				if ( !$order_field ) {
 					$order_field = 'Priority';
 				}
 
 				foreach ($ids as $id) {
 					$object->Load($id);
 
 					switch ( $event->Name ) {
 						case 'OnMassApprove':
 							$object->SetDBField($status_field, 1);
 							break;
 
 						case 'OnMassDecline':
 							$object->SetDBField($status_field, 0);
 							break;
 
 						case 'OnMassMoveUp':
 							$object->SetDBField($order_field, $object->GetDBField($order_field) + 1);
 							break;
 
 						case 'OnMassMoveDown':
 							$object->SetDBField($order_field, $object->GetDBField($order_field) - 1);
 							break;
 					}
 
 					if ( $object->Update() ) {
 						$event->status = kEvent::erSUCCESS;
 					}
 					else {
 						$event->status = kEvent::erFAIL;
 						$event->redirect = false;
 						break;
 					}
 				}
 			}
 
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Clones selected items in list
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnMassClone(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler', Array ('parent_event' => $event));
 			/* @var $temp_handler kTempTablesHandler */
 
 			$ids = $this->StoreSelectedIDs($event);
 
 			if ( $ids ) {
 				$temp_handler->CloneItems($event->Prefix, $event->Special, $ids);
 			}
 
 			$this->clearSelectedIDs($event);
 		}
 
 		/**
 		 * Checks if given value is present in given array
 		 *
 		 * @param Array $records
 		 * @param string $field
 		 * @param mixed $value
 		 * @return bool
 		 * @access protected
 		 */
 		protected function check_array($records, $field, $value)
 		{
 			foreach ($records as $record) {
 				if ($record[$field] == $value) {
 					return true;
 				}
 			}
 
 			return false;
 		}
 
 		/**
 		 * Saves data from editing form to database without checking required fields
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSavePopup(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$this->RemoveRequiredFields($object);
 			$event->CallSubEvent('OnPreSave');
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 /* End of Edit events */
 
 		// III. Events that allow to put some code before and after Update,Load,Create and Delete methods of item
 
 		/**
 		 * Occurs before loading item, 'id' parameter
 		 * allows to get id of item being loaded
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemLoad(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after loading item, 'id' parameter
 		 * allows to get id of item that was loaded
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemLoad(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs before creating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemCreate(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after creating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemCreate(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( !$object->IsTempTable() ) {
 				$this->_processPendingActions($event);
 			}
 		}
 
 		/**
 		 * Occurs before updating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after updating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemUpdate(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( !$object->IsTempTable() ) {
 				$this->_processPendingActions($event);
 			}
 		}
 
 		/**
 		 * Occurs before deleting item, id of item being
 		 * deleted is stored as 'id' event param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemDelete(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after deleting item, id of deleted item
 		 * is stored as 'id' param of event
 		 *
 		 * Also deletes subscriptions to that particual item once it's deleted
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemDelete(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// 1. delete direct subscriptions to item, that was deleted
 			$this->_deleteSubscriptions($event->Prefix, 'ItemId', $object->GetID());
 
 			$sub_items = $this->Application->getUnitOption($event->Prefix, 'SubItems', Array ());
 			/* @var $sub_items Array */
 
 			// 2. delete this item sub-items subscriptions, that reference item, that was deleted
 			foreach ($sub_items as $sub_prefix) {
 				$this->_deleteSubscriptions($sub_prefix, 'ParentItemId', $object->GetID());
 			}
 		}
 
 		/**
 		 * Deletes all subscriptions, associated with given item
 		 *
 		 * @param string $prefix
 		 * @param string $field
 		 * @param int $value
 		 * @return void
 		 * @access protected
 		 */
 		protected function _deleteSubscriptions($prefix, $field, $value)
 		{
 			$sql = 'SELECT TemplateId
 					FROM ' . $this->Application->getUnitOption('email-template', 'TableName') . '
 					WHERE BindToSystemEvent REGEXP "' . $this->Conn->escape($prefix) . '(\\\\.[^:]*:.*|:.*)"';
 			$email_template_ids = $this->Conn->GetCol($sql);
 
 			if ( !$email_template_ids ) {
 				return;
 			}
 
 			// e-mail events, connected to that unit prefix are found
 			$sql = 'SELECT SubscriptionId
 					FROM ' . TABLE_PREFIX . 'SystemEventSubscriptions
 					WHERE ' . $field . ' = ' . $value . ' AND EmailTemplateId IN (' . implode(',', $email_template_ids) . ')';
 			$ids = $this->Conn->GetCol($sql);
 
 			if ( !$ids ) {
 				return;
 			}
 
 			$temp_handler = $this->Application->recallObject('system-event-subscription_TempHandler', 'kTempTablesHandler');
 			/* @var $temp_handler kTempTablesHandler */
 
 			$temp_handler->DeleteItems('system-event-subscription', '', $ids);
 		}
 
 		/**
 		 * Occurs before validation attempt
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemValidate(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after successful item validation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemValidate(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after an item has been copied to temp
 		 * Id of copied item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterCopyToTemp(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs before an item is deleted from live table when copying from temp
 		 * (temp handler deleted all items from live and then copy over all items from temp)
 		 * Id of item being deleted is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeDeleteFromLive(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs before an item is copied to live table (after all foreign keys have been updated)
 		 * Id of item being copied is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeCopyToLive(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after an item has been copied to live table
 		 * Id of copied item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterCopyToLive(kEvent $event)
 		{
 			$object = $event->getObject(array('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$object->SwitchToLive();
 			$object->Load($event->getEventParam('id'));
 
 			$this->_processPendingActions($event);
 		}
 
 		/**
 		 * Processing file pending actions (e.g. delete scheduled files)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function _processPendingActions(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$update_required = false;
 			$temp_id = $event->getEventParam('temp_id');
 			$id = $temp_id !== false ? $temp_id : $object->GetID();
 
 			foreach ($object->getPendingActions($id) as $data) {
 				switch ( $data['action'] ) {
 					case 'delete':
 						unlink($data['file']);
 						break;
 
 					case 'make_live':
 						$file_helper = $this->Application->recallObject('FileHelper');
 						/* @var $file_helper FileHelper */
 
 						if ( !file_exists($data['file']) ) {
 							// file removal was requested too
 							continue;
 						}
 
 						$old_name = basename($data['file']);
 						$new_name = $file_helper->ensureUniqueFilename(dirname($data['file']), kUtil::removeTempExtension($old_name));
 						rename($data['file'], dirname($data['file']) . '/' . $new_name);
 
 						$db_value = $object->GetDBField($data['field']);
 						$object->SetDBField($data['field'], str_replace($old_name, $new_name, $db_value));
 						$update_required = true;
 						break;
 
 					default:
 						trigger_error('Unsupported pending action "' . $data['action'] . '" for "' . $event->getPrefixSpecial() . '" unit', E_USER_WARNING);
 						break;
 				}
 			}
 
 			// remove pending actions before updating to prevent recursion
 			$object->setPendingActions();
 
 			if ( $update_required ) {
 				$object->Update();
 			}
 		}
 
 		/**
 		 * Occurs before an item has been cloned
 		 * Id of newly created item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeClone(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after an item has been cloned
 		 * Id of newly created item is passed as event' 'id' param
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterClone(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Occurs after list is queried
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterListQuery(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Ensures that popup will be closed automatically
 		 * and parent window will be refreshed with template
 		 * passed
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @deprecated
 		 */
 		protected function finalizePopup(kEvent $event)
 		{
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Create search filters based on search query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSearch(kEvent $event)
 		{
 			$event->setPseudoClass('_List');
 
 			$search_helper = $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$search_helper->performSearch($event);
 		}
 
 		/**
 		 * Clear search keywords
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSearchReset(kEvent $event)
 		{
 			$search_helper = $this->Application->recallObject('SearchHelper');
 			/* @var $search_helper kSearchHelper */
 
 			$search_helper->resetSearch($event);
 		}
 
 		/**
 		 * Set's new filter value (filter_id meaning from config)
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @deprecated
 		 */
 		protected function OnSetFilter(kEvent $event)
 		{
 			$filter_id = $this->Application->GetVar('filter_id');
 			$filter_value = $this->Application->GetVar('filter_value');
 
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
 			$view_filter = $view_filter ? unserialize($view_filter) : Array ();
 
 			$view_filter[$filter_id] = $filter_value;
 
 			$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
 		}
 
 		/**
 		 * Sets view filter based on request
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSetFilterPattern(kEvent $event)
 		{
 			$filters = $this->Application->GetVar($event->getPrefixSpecial(true) . '_filters');
 			if ( !$filters ) {
 				return;
 			}
 
 			$view_filter = $this->Application->RecallVar($event->getPrefixSpecial() . '_view_filter');
 			$view_filter = $view_filter ? unserialize($view_filter) : Array ();
 
 			$filters = explode(',', $filters);
 
 			foreach ($filters as $a_filter) {
 				list($id, $value) = explode('=', $a_filter);
 				$view_filter[$id] = $value;
 			}
 
 			$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
 			$event->redirect = false;
 		}
 
 		/**
 		 * Add/Remove all filters applied to list from "View" menu
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function FilterAction(kEvent $event)
 		{
 			$view_filter = Array ();
 			$filter_menu = $this->Application->getUnitOption($event->Prefix, 'FilterMenu');
 
 			switch ($event->Name) {
 				case 'OnRemoveFilters':
 					$filter_value = 1;
 					break;
 
 				case 'OnApplyFilters':
 					$filter_value = 0;
 					break;
 
 				default:
 					$filter_value = 0;
 					break;
 			}
 
 			foreach ($filter_menu['Filters'] as $filter_key => $filter_params) {
 				if ( !$filter_params ) {
 					continue;
 				}
 
 				$view_filter[$filter_key] = $filter_value;
 			}
 
 			$this->Application->StoreVar($event->getPrefixSpecial() . '_view_filter', serialize($view_filter));
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		protected function OnPreSaveAndOpenTranslator(kEvent $event)
 		{
 			$this->Application->SetVar('allow_translation', true);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$this->RemoveRequiredFields($object);
 			$event->CallSubEvent('OnPreSave');
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$resource_id = $this->Application->GetVar('translator_resource_id');
 
 				if ( $resource_id ) {
 					$t_prefixes = explode(',', $this->Application->GetVar('translator_prefixes'));
 
 					$cdata = $this->Application->recallObject($t_prefixes[1], NULL, Array ('skip_autoload' => true));
 					/* @var $cdata kDBItem */
 
 					$cdata->Load($resource_id, 'ResourceId');
 
 					if ( !$cdata->isLoaded() ) {
 						$cdata->SetDBField('ResourceId', $resource_id);
 						$cdata->Create();
 					}
 
 					$this->Application->SetVar($cdata->getPrefixSpecial() . '_id', $cdata->GetID());
 				}
 
 				$event->redirect = $this->Application->GetVar('translator_t');
 
 				$redirect_params = Array (
 					'pass' => 'all,trans,' . $this->Application->GetVar('translator_prefixes'),
 					'opener' => 's',
 					$event->getPrefixSpecial(true) . '_id' => $object->GetID(),
 					'trans_event'		=>	'OnLoad',
 					'trans_prefix'		=>	$this->Application->GetVar('translator_prefixes'),
 					'trans_field' 		=>	$this->Application->GetVar('translator_field'),
 					'trans_multi_line'	=>	$this->Application->GetVar('translator_multi_line'),
 				);
 
 				$event->setRedirectParams($redirect_params);
 
 				// 1. SAVE LAST TEMPLATE TO SESSION (really needed here, because of tweaky redirect)
 				$last_template = $this->Application->RecallVar('last_template');
 				preg_match('/index4\.php\|' . $this->Application->GetSID() . '-(.*):/U', $last_template, $rets);
 				$this->Application->StoreVar('return_template', $this->Application->GetVar('t'));
 			}
 		}
 
 		/**
 		 * Makes all fields non-required
 		 *
 		 * @param kDBItem $object
 		 * @return void
 		 * @access protected
 		 */
 		protected function RemoveRequiredFields(&$object)
 		{
 			// making all field non-required to achieve successful presave
 			$fields = array_keys( $object->getFields() );
 
 			foreach ($fields as $field) {
 				if ( $object->isRequired($field) ) {
 					$object->setRequired($field, false);
 				}
 			}
 		}
 
 		/**
 		 * Saves selected user in needed field
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSelectUser(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar('u');
 
 			if ( $items_info ) {
 				list ($user_id, ) = each($items_info);
 				$this->RemoveRequiredFields($object);
 
 				$is_new = !$object->isLoaded();
 				$is_main = substr($this->Application->GetVar($event->Prefix . '_mode'), 0, 1) == 't';
 
 				if ( $is_new ) {
 					$new_event = $is_main ? 'OnPreCreate' : 'OnNew';
 					$event->CallSubEvent($new_event);
 					$event->redirect = true;
 				}
 
 				$object->SetDBField($this->Application->RecallVar('dst_field'), $user_id);
 
 				if ( $is_new ) {
 					$object->Create();
 				}
 				else {
 					$object->Update();
 				}
 			}
 
 			$event->SetRedirectParam($event->getPrefixSpecial() . '_id', $object->GetID());
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 /** EXPORT RELATED **/
 
 		/**
 		 * Shows export dialog
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnExport(kEvent $event)
 		{
 			$selected_ids = $this->StoreSelectedIDs($event);
 
 			if ( implode(',', $selected_ids) == '' ) {
 				// K4 fix when no ids found bad selected ids array is formed
 				$selected_ids = false;
 			}
 
 			$this->Application->StoreVar($event->Prefix . '_export_ids', $selected_ids ? implode(',', $selected_ids) : '');
 
 			$this->Application->LinkVar('export_finish_t');
 			$this->Application->LinkVar('export_progress_t');
 			$this->Application->StoreVar('export_special', $event->Special);
 			$this->Application->StoreVar('export_grid', $this->Application->GetVar('grid', 'Default'));
 
 			$redirect_params = Array (
 				$this->Prefix . '.export_event' => 'OnNew',
 				'pass' => 'all,' . $this->Prefix . '.export'
 			);
 
 			$event->setRedirectParams($redirect_params);
 		}
 
 		/**
 		 * Apply some special processing to object being
 		 * recalled before using it in other events that
 		 * call prepareObject
 		 *
 		 * @param kDBItem|kDBList $object
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function prepareObject(&$object, kEvent $event)
 		{
 			if ( $event->Special == 'export' || $event->Special == 'import' ) {
 				$export_helper = $this->Application->recallObject('CatItemExportHelper');
 				/* @var $export_helper kCatDBItemExportHelper */
 
 				$export_helper->prepareExportColumns($event);
 			}
 		}
 
 		/**
 		 * Returns specific to each item type columns only
 		 *
 		 * @param kEvent $event
 		 * @return Array
 		 * @access public
 		 */
 		public function getCustomExportColumns(kEvent $event)
 		{
 			return Array ();
 		}
 
 		/**
 		 * Export form validation & processing
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnExportBegin(kEvent $event)
 		{
 			$export_helper = $this->Application->recallObject('CatItemExportHelper');
 			/* @var $export_helper kCatDBItemExportHelper */
 
 			$export_helper->OnExportBegin($event);
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnExportCancel(kEvent $event)
 		{
 			$this->OnGoBack($event);
 		}
 
 		/**
 		 * Allows configuring export options
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeExportBegin(kEvent $event)
 		{
 
 		}
 
 		/**
 		 * Deletes export preset
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnDeleteExportPreset(kEvent $event)
 		{
 			$field_values = $this->getSubmittedFields($event);
 
 			if ( !$field_values ) {
 				return ;
 			}
 
 			$preset_key = $field_values['ExportPresets'];
 			$export_settings = $this->Application->RecallPersistentVar('export_settings');
 
 			if ( !$export_settings ) {
 				return ;
 			}
 
 			$export_settings = unserialize($export_settings);
 
 			if ( !isset($export_settings[$event->Prefix]) ) {
 				return ;
 			}
 
 			$to_delete = '';
 
 			foreach ($export_settings[$event->Prefix] as $key => $val) {
 				if ( implode('|', $val['ExportColumns']) == $preset_key ) {
 					$to_delete = $key;
 					break;
 				}
 			}
 
 			if ( $to_delete ) {
 				unset($export_settings[$event->Prefix][$to_delete]);
 				$this->Application->StorePersistentVar('export_settings', serialize($export_settings));
 			}
 		}
 
 		/**
 		 * Saves changes & changes language
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreSaveAndChangeLanguage(kEvent $event)
 		{
 			if ( $this->UseTempTables($event) ) {
 				$event->CallSubEvent('OnPreSave');
 			}
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$this->Application->SetVar('m_lang', $this->Application->GetVar('language'));
 
 				$data = $this->Application->GetVar('st_id');
 
 				if ( $data ) {
 					$event->SetRedirectParam('st_id', $data);
 				}
 			}
 		}
 
 		/**
 		 * Used to save files uploaded via swfuploader
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnUploadFile(kEvent $event)
 		{
 			$event->status = kEvent::erSTOP;
 //			define('DBG_SKIP_REPORTING', 0);
 			$default_msg = "Flash requires that we output something or it won't fire the uploadSuccess event";
 
 			if ( !$this->Application->HttpQuery->Post ) {
 				// Variables {field, id, flashsid} are always submitted through POST!
 				// When file size is larger, then "upload_max_filesize" (in php.ini),
 				// then these variables also are not submitted -> handle such case.
 				header('HTTP/1.0 413 File size exceeds allowed limit');
 				echo $default_msg;
 				return;
 			}
 
 			if ( !$this->_checkFlashUploaderPermission($event) ) {
 				// 403 Forbidden
 				header('HTTP/1.0 403 You don\'t have permissions to upload');
 				echo $default_msg;
 				return;
 			}
 
 			$value = $this->Application->GetVar('Filedata');
 
 			if ( !$value || ($value['error'] != UPLOAD_ERR_OK) ) {
 				// 413 Request Entity Too Large (file uploads disabled OR uploaded file was
 				// to large for web server to accept, see "upload_max_filesize" in php.ini)
 				header('HTTP/1.0 413 File size exceeds allowed limit');
 				echo $default_msg;
 				return;
 			}
 
 			$value = $this->Application->HttpQuery->unescapeRequestVariable($value);
 
 			$tmp_path = WRITEABLE . '/tmp/';
 			$filename = $value['name'] . '.tmp';
 			$id = $this->Application->GetVar('id');
 
 			if ( $id ) {
 				$filename = $id . '_' . $filename;
 			}
 
 			if ( !is_writable($tmp_path) ) {
 				// 500 Internal Server Error
 				// check both temp and live upload directory
 				header('HTTP/1.0 500 Write permissions not set on the server');
 				echo $default_msg;
 				return;
 			}
 
 			$file_helper = $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$filename = $file_helper->ensureUniqueFilename($tmp_path, $filename);
 			$storage_format = $this->_getStorageFormat($this->Application->GetVar('field'), $event);
 
 			if ( $storage_format ) {
 				$image_helper = $this->Application->recallObject('ImageHelper');
 				/* @var $image_helper ImageHelper */
 
 				move_uploaded_file($value['tmp_name'], $value['tmp_name'] . '.jpg'); // add extension, so ResizeImage can work
 				$url = $image_helper->ResizeImage($value['tmp_name'] . '.jpg', $storage_format);
 				$tmp_name = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', '/', $url);
 				rename($tmp_name, $tmp_path . $filename);
 			}
 			else {
 				move_uploaded_file($value['tmp_name'], $tmp_path . $filename);
 			}
 
 			echo preg_replace('/^' . preg_quote($id, '/') . '_/', '', $filename);
 
 			$this->deleteTempFiles($tmp_path);
 
 			$thumbs_path = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $tmp_path, 1);
 			$thumbs_path = FULL_PATH . THUMBS_PATH . $thumbs_path;
 
 			if ( file_exists($thumbs_path) ) {
 				$this->deleteTempFiles($thumbs_path);
 			}
 		}
 
 		/**
 		 * Gets storage format for a given field
 		 *
 		 * @param string $field_name
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function _getStorageFormat($field_name, kEvent $event)
 		{
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 			$virtual_fields = $this->Application->getUnitOption($event->Prefix, 'VirtualFields');
 			$field_options = array_key_exists($field_name, $fields) ? $fields[$field_name] : $virtual_fields[$field_name];
 
 			return isset($field_options['storage_format']) ? $field_options['storage_format'] : false;
 		}
 
 		/**
 		 * Delete temporary files, that won't be used for sure
 		 *
 		 * @param string $path
 		 * @return void
 		 * @access protected
 		 */
 		protected function deleteTempFiles($path)
 		{
 			$files = glob($path . '*.*');
 			$max_file_date = strtotime('-1 day');
 
 			foreach ( $files as $file ) {
 				if ( filemtime($file) < $max_file_date ) {
 					unlink($file);
 				}
 			}
 		}
 
 		/**
 		 * Checks, that flash uploader is allowed to perform upload
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 */
 		protected function _checkFlashUploaderPermission(kEvent $event)
 		{
 			// Flash uploader does NOT send correct cookies, so we need to make our own check
 			$cookie_name = 'adm_' . $this->Application->ConfigValue('SessionCookieName');
 			$this->Application->HttpQuery->Cookie['cookies_on'] = 1;
 			$this->Application->HttpQuery->Cookie[$cookie_name] = $this->Application->GetVar('flashsid');
 
 			// this prevents session from auto-expiring when KeepSessionOnBrowserClose & FireFox is used
 			$this->Application->HttpQuery->Cookie[$cookie_name . '_live'] = $this->Application->GetVar('flashsid');
 
 			$admin_ses = $this->Application->recallObject('Session.admin');
 			/* @var $admin_ses Session */
 
 			if ( $admin_ses->RecallVar('user_id') == USER_ROOT ) {
 				return true;
 			}
 
 			// copy some data from given session to current session
 			$backup_user_id = $this->Application->RecallVar('user_id');
 			$this->Application->StoreVar('user_id', $admin_ses->RecallVar('user_id'));
 
 			$backup_user_groups = $this->Application->RecallVar('UserGroups');
 			$this->Application->StoreVar('UserGroups', $admin_ses->RecallVar('UserGroups'));
 
 			// check permissions using event, that have "add|edit" rule
 			$check_event = new kEvent($event->getPrefixSpecial() . ':OnProcessSelected');
 			$check_event->setEventParam('top_prefix', $this->Application->GetTopmostPrefix($event->Prefix, true));
 			$allowed_to_upload = $this->CheckPermission($check_event);
 
 			// restore changed data, so nothing gets saved to database
 			$this->Application->StoreVar('user_id', $backup_user_id);
 			$this->Application->StoreVar('UserGroups', $backup_user_groups);
 
 			return $allowed_to_upload;
 		}
 
 		/**
 		 * Remembers, that file should be deleted on item's save from temp table
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnDeleteFile(kEvent $event)
 		{
 			$event->status = kEvent::erSTOP;
 			$filename = $this->_getSafeFilename();
 
 			if ( !$filename ) {
 				return;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$field_id = $this->Application->GetVar('field_id');
 
 			if ( !preg_match_all('/\[([^\[\]]*)\]/', $field_id, $regs) ) {
 				return;
 			}
 
 			$field = $regs[1][1];
 			$record_id = $regs[1][0];
 			$pending_actions = $object->getPendingActions($record_id);
 			$upload_dir = $object->GetFieldOption($field, 'upload_dir');
 
 			$pending_actions[] = Array (
 				'action' => 'delete', 'id' => $record_id, 'field' => $field, 'file' => FULL_PATH . $upload_dir . $filename
 			);
 
 			$object->setPendingActions($pending_actions, $record_id);
 		}
 
 		/**
 		 * Returns url for viewing uploaded file
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnViewFile(kEvent $event)
 		{
 			$event->status = kEvent::erSTOP;
 			$filename = $this->_getSafeFilename();
 
 			if ( !$filename ) {
 				return;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$field = $this->Application->GetVar('field');
 			$options = $object->GetFieldOptions($field);
 
 			// set current uploaded file
 			if ( $this->Application->GetVar('tmp') ) {
 				$options['upload_dir'] = WRITEBALE_BASE . '/tmp/';
 				unset($options['include_path']);
 				$object->SetFieldOptions($field, $options);
 
 				$object->SetDBField($field, $this->Application->GetVar('id') . '_' . $filename);
 			}
 			else {
 				$object->SetDBField($field, $filename);
 			}
 
 			// get url to uploaded file
 			if ( $this->Application->GetVar('thumb') ) {
 				$url = $object->GetField($field, $options['thumb_format']);
 			}
 			else {
 				$url = $object->GetField($field, 'raw_url');
 			}
 
 			$file_helper = $this->Application->recallObject('FileHelper');
 			/* @var $file_helper FileHelper */
 
 			$path = $file_helper->urlToPath($url);
 
 			if ( !file_exists($path) ) {
 				exit;
 			}
 
 			header('Content-Length: ' . filesize($path));
 			$this->Application->setContentType(kUtil::mimeContentType($path), false);
 			header('Content-Disposition: inline; filename="' . kUtil::removeTempExtension($filename) . '"');
 
 			readfile($path);
 		}
 
 		/**
 		 * Returns safe version of filename specified in url
 		 *
 		 * @return bool|string
 		 * @access protected
 		 */
 		protected function _getSafeFilename()
 		{
 			$filename = $this->Application->GetVar('file');
 			$filename = $this->Application->unescapeRequestVariable($filename);
 
 			if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) {
 				// when relative paths or special chars are found template names from url, then it's hacking attempt
 				return false;
 			}
 
 			return $filename;
 		}
 
 		/**
 		 * Validates MInput control fields
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnValidateMInputFields(kEvent $event)
 		{
 			$minput_helper = $this->Application->recallObject('MInputHelper');
 			/* @var $minput_helper MInputHelper */
 
 			$minput_helper->OnValidateMInputFields($event);
 		}
 
 		/**
 		 * Validates individual object field and returns the result
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnValidateField(kEvent $event)
 		{
 			$event->status = kEvent::erSTOP;
 			$field = $this->Application->GetVar('field');
 
 			if ( ($this->Application->GetVar('ajax') != 'yes') || !$field ) {
 				return;
 			}
 
 			$object = $event->getObject(Array ('skip_autoload' => true));
 			/* @var $object kDBItem */
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			if ( !$items_info ) {
 				return;
 			}
 
 			list ($id, $field_values) = each($items_info);
 			$object->Load($id);
 			$object->SetFieldsFromHash($field_values);
 			$event->setEventParam('form_data', $field_values);
 			$object->setID($id);
 
 			$response = Array ('status' => 'OK');
 
 			$event->CallSubEvent($object->isLoaded() ? 'OnBeforeItemUpdate' : 'OnBeforeItemCreate');
 
 			// validate all fields, since "Password_plain" field sets error to "Password" field, which is passed here
 			$error_field = $object->GetFieldOption($field, 'error_field', false, $field);
 
 			if ( !$object->Validate() && $object->GetErrorPseudo($error_field) ) {
 				$response['status'] = $object->GetErrorMsg($error_field, false);
 			}
 
 			$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
 			/* @var $ajax_form_helper AjaxFormHelper */
 
 			$response['other_errors'] = $ajax_form_helper->getErrorMessages($object);
 			$response['uploader_info'] = $ajax_form_helper->getUploaderInfo($object, array_keys($field_values));
 
 			$event->status = kEvent::erSTOP; // since event's OnBefore... events can change this event status
 			echo json_encode($response);
 		}
 
 		/**
 		 * Returns auto-complete values for ajax-dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSuggestValues(kEvent $event)
 		{
-			if ( !$this->Application->isAdminUser ) {
-				// very careful here, because this event allows to
-				// view every object field -> limit only to logged-in admins
-				return;
-			}
-
 			$event->status = kEvent::erSTOP;
 
 			$field = $this->Application->GetVar('field');
 			$cur_value = $this->Application->GetVar('cur_value');
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 
 			$object = $event->getObject();
 
 			if ( !$field || !$cur_value || !$object->isField($field) ) {
 				return;
 			}
 
 			$limit = $this->Application->GetVar('limit');
 			if ( !$limit ) {
 				$limit = 20;
 			}
 
 			$sql = 'SELECT DISTINCT ' . $field . '
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE ' . $field . ' LIKE ' . $this->Conn->qstr($cur_value . '%') . '
 					ORDER BY ' . $field . '
 					LIMIT 0,' . $limit;
 			$data = $this->Conn->GetCol($sql);
 
 			$this->Application->XMLHeader();
 
 			echo '<suggestions>';
 
 			foreach ($data as $item) {
 				echo '<item>' . kUtil::escape($item, kUtil::ESCAPE_HTML) . '</item>';
 			}
 
 			echo '</suggestions>';
 		}
 
 		/**
 		 * Enter description here...
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSaveWidths(kEvent $event)
 		{
 			$event->status = kEvent::erSTOP;
 
 //			$this->Application->setContentType('text/xml');
 
 			$picker_helper = new kColumnPickerHelper(
 				$event->getPrefixSpecial(),
 				$this->Application->GetVar('grid_name')
 			);
 
 			$picker_helper->saveWidths($this->Application->GetVar('widths'));
 
 			echo 'OK';
 		}
 
 		/**
 		 * Called from CSV import script after item fields
 		 * are set and validated, but before actual item create/update.
 		 * If event status is kEvent::erSUCCESS, line will be imported,
 		 * else it will not be imported but added to skipped lines
 		 * and displayed in the end of import.
 		 * Event status is preset from import script.
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeCSVLineImport(kEvent $event)
 		{
 			// abstract, for hooking
 		}
 
 		/**
 		 * [HOOK] Allows to add cloned subitem to given prefix
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnCloneSubItem(kEvent $event)
 		{
 			$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
 
 			$subitem_prefix = $event->Prefix . '-' . preg_replace('/^#/', '', $event->MasterEvent->Prefix);
 			$clones[$subitem_prefix] = Array ('ParentPrefix' => $event->Prefix);
 			$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
 		}
 
 		/**
 		 * Returns constrain for priority calculations
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @see PriorityEventHandler
 		 * @access protected
 		 */
 		protected function OnGetConstrainInfo(kEvent $event)
 		{
 			$event->setEventParam('constrain_info', Array ('', ''));
 		}
 	}
Index: branches/5.2.x/core/units/helpers/permissions_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/permissions_helper.php	(revision 16413)
+++ branches/5.2.x/core/units/helpers/permissions_helper.php	(revision 16414)
@@ -1,839 +1,847 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class kPermissionsHelper extends kHelper {
 
 		/**
 		 * Current set of permissions for group being edited
 		 *
 		 * @var Array
 		 */
 		var $Permissions = Array();
 
 		function LoadPermissions($group_id, $cat_id, $type = 1, $prefix = '')
 		{
 			$perm_table = $this->Application->getUnitOption('perm', 'TableName');
 			$perm_table = $this->Application->GetTempName($perm_table, 'prefix:'.$prefix);
 			$sql = 'SELECT *
 					FROM '.$perm_table.'
 					WHERE (GroupId = '.$group_id.') AND (CatId = '.$cat_id.') AND (Type = '.$type.')';
 			$permissions = $this->Conn->Query($sql, 'Permission');
 
 			$this->Permissions = Array();
 			foreach ($permissions as $perm_name => $perm_options) {
 				$perm_record['value'] = $perm_options['PermissionValue'];
 				$perm_record['id'] = $perm_options['PermissionId'];
 				$this->Permissions[$perm_name] = $perm_record;
 			}
 		}
 
 		function getPermissionValue($perm_name)
 		{
 			 return isset($this->Permissions[$perm_name]) ? $this->Permissions[$perm_name]['value'] : 0;
 		}
 
 		function getPermissionID($perm_name)
 		{
 			return isset($this->Permissions[$perm_name]) ? $this->Permissions[$perm_name]['id'] : 0;
 		}
 
 		/**
 		 * This is old permission like ADMIN or LOGIN
 		 *
 		 * @param string $section_name
 		 * @param string $perm_name
 		 * @return bool
 		 */
 		function isOldPermission($section_name, $perm_name)
 		{
 			return $section_name == 'in-portal:root' && $perm_name != 'view';
 		}
 
 		/**
 		 * Returns permission names to check based on event name and item prefix (main item or subitem)
 		 *
 		 * @param kEvent $event
 		 * @param Array $perm_mapping
 		 * @return Array
 		 */
 		function getPermissionByEvent($event, $perm_mapping)
 		{
 			$top_prefix = $event->getEventParam('top_prefix');
 
 			$prefix_type = ($top_prefix == $event->Prefix) ? 'self' : 'subitem';
 			$perm_mapping = getArrayValue($perm_mapping, $event->Name);
 
 			if (!$perm_mapping[$prefix_type]) {
 				throw new Exception('Permission mappings not defined for event <strong>' . $top_prefix . ' <- ' . $event->Prefix . ':' . $event->Name . '</strong>');
 			}
 
 			if ($perm_mapping[$prefix_type] === true) {
 				// event is defined in mapping but is not checked by permissions
 				return true;
 			}
 
 			return explode('|', $perm_mapping[$prefix_type]);
 		}
 
 		/**
 		 * Common event permission checking method
 		 *
 		 * @param kEvent $event
 		 * @param Array $perm_mapping
 		 * @return bool
 		 */
 		function CheckEventPermission($event, $perm_mapping)
 		{
 			$section = $event->getSection();
 			if (preg_match('/^CATEGORY:(.*)/', $section)) {
 				return $this->CheckEventCategoryPermission($event, $perm_mapping);
 			}
 
 			$top_prefix = $event->getEventParam('top_prefix');
 			$check_perms = $this->getPermissionByEvent($event, $perm_mapping);
 
 			if ($check_perms === true) {
 				// event is defined in mapping but is not checked by permissions
 				return true;
 			}
 
 			$perm_status = false;
 			foreach ($check_perms as $perm_name) {
 				// check if at least one of required permissions is set
 				if ($perm_name == 'debug' && $this->Application->isDebugMode(false)) {
 					// universal "debug" permission
 					return true;
 				}
+				elseif ( $perm_name == 'admin' && $this->Application->isAdminUser ) {
+					// any logged-in admin user will suffice
+					return true;
+				}
 
 				$perm_name = $section.'.'.$perm_name;
 				$perm_status = $this->CheckPermission($perm_name, 1);
 				if (($perm_name == $section.'.add') && $perm_status && ($top_prefix == $event->Prefix)) {
 					// main item, add permission allowed, but ID is > 0, then deny permission
 					// how to get id here
 				}
 
 				if ($perm_status) {
 					return $perm_status;
 				}
 			}
 
 			return $this->finalizePermissionCheck($event, $perm_status);
 		}
 
 		/**
 		 * Returns owner + primary category for each item (used for permission checking)
 		 *
 		 * @param string $prefix
 		 * @param string $ids
 		 * @param bool $temp_mode
 		 * @return Array
 		 * @author Alex
 		 */
 		function GetCategoryItemData($prefix, $ids, $temp_mode = false)
 		{
 			if (is_array($ids)) {
 				$ids = implode(',', $ids);
 			}
 			$id_field = $this->Application->getUnitOption($prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			$ci_table = $this->Application->getUnitOption('ci', 'TableName');
 
 			if ($temp_mode) {
 				$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $prefix);
 				$ci_table = $this->Application->GetTempName($ci_table, 'prefix:' . $prefix);
 			}
 
 			$owner_field = $this->Application->getUnitOption($prefix, 'OwnerField');
 			if (!$owner_field) {
 				$owner_field = 'CreatedById';
 			}
 
 			$sql = 'SELECT item_table.'.$id_field.', item_table.'.$owner_field.' AS CreatedById, ci.CategoryId
 					FROM '.$table_name.' item_table
 					LEFT JOIN '.$ci_table.' ci ON ci.ItemResourceId = item_table.ResourceId
 					WHERE item_table.'.$id_field.' IN ('.$ids.') AND (ci.PrimaryCat = 1)';
 			return $this->Conn->Query($sql, $id_field);
 		}
 
 		/**
 		 * Check category-based permissions for category items
 		 *
 		 * @param kEvent $event
 		 * @param Array $event_perm_mapping
 		 * @return bool
 		 */
 		function _frontCheckEventCategoryPermission($event, $event_perm_mapping)
 		{
 			// mapping between specific permissions and common permissions
 			static $perm_mapping = Array(
 				'add' => 'ADD', 'add.pending' => 'ADD.PENDING', 'edit' => 'MODIFY',
 				'edit.pending' => 'MODIFY.PENDING', 'delete' => 'DELETE', 'view' => 'VIEW',
-				'debug' => 'DEBUG'
+				'debug' => 'DEBUG', 'admin' => 'ADMIN',
 			);
 
 			$top_prefix = $event->getEventParam('top_prefix');
 			$event_handler = $this->Application->recallObject($event->Prefix . '_EventHandler');
 			/* @var $event_handler kCatDBEventHandler */
 
 			$raise_warnings = $event->getEventParam('raise_warnings');
 			$event->setEventParam('raise_warnings', 0);
 			if ( $event->Prefix != $top_prefix ) {
 				$top_event = new kEvent($top_prefix . ':' . $event->Name);
 				$id = $event_handler->getPassedID($top_event);
 			}
 			else {
 				$id = $event_handler->getPassedID($event);
 			}
 			$event->setEventParam('raise_warnings', $raise_warnings);
 
 			$owner_id = USER_ROOT; // owner is root if not detected
 			if ( !$id ) {
 				// item being created -> check by current (before editing started, saved in OnPreCreate event) category permissions
 				// note: category in session is placed on catalog data import start
 				$category_id = $this->Application->isAdmin ? $this->Application->RecallVar('m_cat_id') : $this->Application->GetVar('m_cat_id');
 			}
 			elseif ( $top_prefix == 'c' || $top_prefix == 'st' ) {
 				$category_id = $id;
 			}
 			else {
 				// item being edited -> check by it's primary category permissions
 				$items_info = $this->GetCategoryItemData($top_prefix, $id);
 
 				if ( $items_info ) {
 					$category_id = $items_info[$id]['CategoryId'];
 					$owner_id = $items_info[$id]['CreatedById'];
 				}
 				else {
 					// item wasn't found in database
 					$category_id = $this->Application->GetVar('m_cat_id');
 				}
 			}
 
 			// specific permission check for pending & owner permissions: begin
 			$uploader_events = Array ('OnUploadFile', 'OnDeleteFile', 'OnViewFile');
 			if ( in_array($event->Name, $uploader_events) ) {
 				// don't recall target object during uploader-related, because OnItemLoad will use incorrect
 				// $user_id in Firefox (during Flash problems session will be used from Internet Exploere)
 				$new_item = false;
 			}
 			else {
 				$new_item = $this->Application->isAdminUser && $event_handler->isNewItemCreate($event) ? true : false;
 				$check_status = $this->checkCombinedPermissions($event, $owner_id, (int)$category_id, $new_item);
 			}
 
 			if ( isset($check_status) ) {
 				return $this->finalizePermissionCheck($event, $check_status);
 			}
 			// specific permission check for pending & owner permissions: end
 
 			$perm_status = false;
 			$check_perms = $this->getPermissionByEvent($event, $event_perm_mapping);
 
 			if ( $check_perms === true ) {
 				// event is defined in mapping but is not checked by permissions
 				return true;
 			}
 
 			$item_prefix = $this->Application->getUnitOption($top_prefix, 'PermItemPrefix');
 			foreach ($check_perms as $perm_name) {
 				// check if at least one of required permissions is set
 				if ( !isset($perm_mapping[$perm_name]) ) {
 					// not mapped permission (e.g. advanced:approve) -> skip
 					continue;
 				}
 
 				if ( $perm_name == 'debug' && $this->Application->isDebugMode(false) ) {
 					// universal "debug" permission
 					return true;
 				}
+				elseif ( $perm_name == 'admin' && $this->Application->isAdminUser ) {
+					// any logged-in admin user will suffice
+					return true;
+				}
 
 				$perm_name = $item_prefix . '.' . $perm_mapping[$perm_name];
 				$perm_status = $this->CheckPermission($perm_name, 0, (int)$category_id);
 
 				if ( $perm_status ) {
 					return $perm_status;
 				}
 			}
 
 			return $this->finalizePermissionCheck($event, $perm_status);
 		}
 
 		/**
 		 * Finalizes permission checking (with additional debug output, when in debug mode)
 		 *
 		 * @param kEvent $event
 		 * @param bool $perm_status
 		 * @return bool
 		 */
 		function finalizePermissionCheck($event, $perm_status)
 		{
 			if (!$perm_status) {
 				if (MOD_REWRITE) {
 //					$event->SetRedirectParam('m_cat_id', 0); // category means nothing on admin login screen
 					$event->SetRedirectParam('next_template', 'external:' . $_SERVER['REQUEST_URI']);
 				}
 				else {
 					$event->SetRedirectParam('next_template', $this->Application->GetVar('t'));
 				}
 
 				if ($this->Application->isDebugMode()) {
 					// for debugging purposes
 					$event->SetRedirectParam('section', $event->getSection());
 					$event->SetRedirectParam('main_prefix', $event->getEventParam('top_prefix'));
 					$event->SetRedirectParam('event_name', $event->Name);
 				}
 
 				$event->status = kEvent::erPERM_FAIL;
 			}
 
 			return $perm_status;
 		}
 
 		/**
 		 * Allows to check combined permissions (*.owner, *.pending) for add/modify/delete operations from admin & front-end
 		 *
 		 * @param kEvent $event
 		 * @param int $owner_id
 		 * @param int $category_id
 		 * @param bool $new_item
 		 * @return mixed
 		 */
 		function checkCombinedPermissions($event, $owner_id, $category_id, $new_item = false)
 		{
 			$ret = null; // true/false when used, null when not used
 			$top_prefix = $event->getEventParam('top_prefix');
 
 			// check admin permission
 			if (substr($event->Name, 0, 9) == 'OnPreSave') {
 				if ($new_item) {
 					$ret = $this->AddCheckPermission($category_id, $top_prefix);
 				}
 				else {
 					// add & modify because $new_item is false, when item is aready created & then saved in temp table (even with 0 id)
 					$ret =	$this->AddCheckPermission($category_id, $top_prefix) ||
 							$this->ModifyCheckPermission($owner_id, $category_id, $top_prefix);
 				}
 			}
 
 			// check front-end permissions
 			switch ($event->Name) {
 				case 'OnCreate':
 					$ret = $this->AddCheckPermission($category_id, $top_prefix);
 					break;
 
 				case 'OnUpdate':
 					$ret = $this->ModifyCheckPermission($owner_id, $category_id, $top_prefix);
 					break;
 
 				case 'OnDelete':
 				case 'OnMassDelete':
 					$ret = $this->DeleteCheckPermission($owner_id, $category_id, $top_prefix);
 					break;
 			}
 
 			if ($ret === 0) {
 				// permission check failed (user has no permission)
 				$event->status = kEvent::erPERM_FAIL;
 			}
 
 			return $ret;
 		}
 
 		/**
 		 * Simplified permission check for category items, when adding/editing them from advanced view.
 		 *
 		 * @param kEvent $event
 		 * @param Array $event_perm_mapping
 		 * @return mixed
 		 */
 		function CheckEventCategoryPermission($event, $event_perm_mapping)
 		{
 			if (!$this->Application->isAdmin) {
 				// check front-end permission by old scheme
 				return $this->_frontCheckEventCategoryPermission($event, $event_perm_mapping);
 			}
 
 			if (substr($event->Name, 0, 9) == 'OnPreSave') {
 				// check separately, because permission mapping is not defined for OnPreSave* events
 				$check_perms = Array ('add', 'edit');
 			}
 			else {
 				$check_perms = $this->getPermissionByEvent($event, $event_perm_mapping);
 			}
 
 			if ($check_perms === true) {
 				// event is defined in mapping but is not checked by permissions
 				return true;
 			}
 
 			// 1. most of events does require admin login only
 			$perm_status = $this->Application->isAdminUser;
 
 			// 2. in case, when event require more, then "view" right, then restrict it to temporary tables only
 			if (!in_array('view', $check_perms)) {
 				$perm_status = $perm_status && $this->Application->IsTempMode($event->Prefix, $event->Special);
 			}
 
 			return $this->finalizePermissionCheck($event, $perm_status);
 		}
 
 		function TagPermissionCheck($params, $is_owner = false)
 		{
 			$perm_prefix = getArrayValue($params, 'perm_prefix');
 			$perm_event = getArrayValue($params, 'perm_event');
 			$permission_groups = getArrayValue($params, 'permissions');
 			$check_admin = isset($params['admin']) && $params['admin'];
 
 			if ($permission_groups && !$perm_event) {
 				// check permissions by permission names in current category
 				$permission_groups = explode('|', $permission_groups);
 				$group_has_permission = false;
 
 				$perm_category = isset($params['cat_id']) ? $params['cat_id'] : $this->Application->GetVar('m_cat_id');
 
 				if ($perm_prefix) {
 					// use primary category of item with id from {perm_prefix}_id as base for permission checking
 					$perm_category = $this->getPrimaryCategory($perm_prefix);
 				}
 
 				$is_system = isset($params['system']) && $params['system'] ? 1 : 0;
 				foreach ($permission_groups as $permission_group) {
 					$has_permission = true;
 					$permissions = explode(',', $permission_group);
 
 					if ( $check_admin ) {
 						foreach ($permissions as $permission) {
 							$owner_checked = (strpos($permission, '.OWNER.') !== false) ? $is_owner : true;
 							$has_permission = $has_permission && $this->CheckAdminPermission($permission, $is_system, $perm_category) && $owner_checked;
 						}
 					}
 					else {
 						foreach ($permissions as $permission) {
 							$owner_checked = (strpos($permission, '.OWNER.') !== false) ? $is_owner : true;
 							$has_permission = $has_permission && $this->CheckPermission($permission, $is_system, $perm_category) && $owner_checked;
 						}
 					}
 
 					$group_has_permission = $group_has_permission || $has_permission;
 
 					if ($group_has_permission) {
 						return true;
 					}
 				}
 				return false;
 			}
 			elseif ($perm_event) {
 				// check permission by event name
 				list ($prefix, ) = explode(':', $perm_event);
 
 				$event_handler = $this->Application->recallObject($prefix . '_EventHandler');
 				/* @var $event_handler kEventHandler */
 
 				return $event_handler->CheckPermission( new kEvent($perm_event) );
 			}
 
 			return true;
 		}
 
 		/**
 		 * Returns item's primary category (get item_id from request)
 		 *
 		 * @param string $prefix
 		 * @return int
 		 */
 		function getPrimaryCategory($prefix)
 		{
 			$id_field = $this->Application->getUnitOption($prefix, 'IDField');
 			$table_name = $this->Application->getUnitOption($prefix, 'TableName');
 			$id = $this->Application->GetVar($prefix.'_id');
 
 			if (!$id) {
 				return $this->Application->GetVar('m_cat_id');
 			}
 
 			$sql = 'SELECT ResourceId
 					FROM '.$table_name.'
 					WHERE '.$id_field.' = '.(int)$id;
 			$resource_id = $this->Conn->GetOne($sql);
 
 			$sql = 'SELECT CategoryId
 					FROM '.$this->Application->getUnitOption('ci', 'TableName').'
 					WHERE ItemResourceId = '.$resource_id.' AND PrimaryCat = 1';
 			return $this->Conn->GetOne($sql);
 		}
 
 		/**
 		 * Returns no permission template to redirect to
 		 *
 		 * @param Array $params
 		 * @return Array
 		 */
 		function getPermissionTemplate($params)
 		{
 			$t = $this->Application->GetVar('t');
 			$next_t = getArrayValue($params, 'next_template');
 
 			if ( $next_t ) {
 				$t = $next_t;
 			}
 
 			$redirect_params = $this->Application->HttpQuery->getRedirectParams(true);
 
 			if (array_key_exists('pass_category', $params)) {
 				$redirect_params['pass_category'] = $params['pass_cateogry'];
 			}
 
 			if (MOD_REWRITE) {
 				// TODO: $next_t variable is ignored !!! (is anyone using m_RequireLogin tag with "next_template" parameter?)
 				$redirect_params = Array (
 					'm_cat_id' => 0, // category means nothing on admin login screen
 					'next_template' => 'external:' . $_SERVER['REQUEST_URI'],
 				);
 			}
 			else {
 				$redirect_params['next_template'] = $t;
 			}
 
 			if ($this->Application->isAdmin) {
 				$redirect_params['m_wid'] = ''; // remove wid, otherwise parent window may add wid to its name breaking all the frameset (for <a> targets)
 				$redirect_params['pass'] = 'm'; // don't pass any other (except "m") prefixes to admin login template
 			}
 
 			if (!$this->Application->LoggedIn()) {
 				$redirect_template = array_key_exists('login_template', $params) ? $params['login_template'] : '';
 
 				if (!$redirect_template && $this->Application->isAdmin) {
 					$redirect_template = 'login';
 				}
 			}
 			else {
 				if (array_key_exists('no_permissions_template', $params)) {
 					$redirect_template = $params['no_permissions_template'];
 				}
 				else {
 					$redirect_template = $this->Application->isAdmin ? 'no_permission' : $this->Application->ConfigValue('NoPermissionTemplate');
 				}
 
 				if ($this->Application->isDebugMode()) {
 					$redirect_params['from_template'] = 1;
 					$redirect_params['perms'] = $params[ isset($params['permissions']) ? 'permissions' : 'perm_event' ];
 				}
 			}
 
 			if (isset($params['index_file']) && $params['index_file']) {
 				$redirect_params['index_file'] = $params['index_file'];
 			}
 
 			return Array ($redirect_template, $redirect_params);
 		}
 
 		/**
 		 * Check current user permissions based on it's group permissions in specified category (for non-system permissions) or just checks if system permission is set
 		 *
 		 * @param string $name permission name
 		 * @param int $cat_id category id, current used if not specified
 		 * @param int $type permission type {1 - system, 0 - per category}
 		 * @return int
 		 */
 		function CheckPermission($name, $type = 1, $cat_id = null)
 		{
 			$user_id = $this->Application->RecallVar('user_id');
 			return $this->CheckUserPermission($user_id, $name, $type, $cat_id);
 		}
 
 		/**
 		 * Check current admin permissions (when called from Front-End) based on it's group permissions in specified category (for non-system permissions) or just checks if system permission is set
 		 *
 		 * @param string $name permission name
 		 * @param int $cat_id category id, current used if not specified
 		 * @param int $type permission type {1 - system, 0 - per category}
 		 * @return int
 		 */
 		function CheckAdminPermission($name, $type = 1, $cat_id = null)
 		{
 			if ( $this->Application->isAdmin ) {
 				return $this->CheckPermission($name, $type, $cat_id);
 			}
 
 			$user_id = $this->Application->RecallVar('admin_user_id');
 			return $this->CheckUserPermission($user_id, $name, $type, $cat_id);
 		}
 
 		function CheckUserPermission($user_id, $name, $type = 1, $cat_id = null)
 		{
 			$user_id = (int)$user_id;
 
 			if ( $user_id == USER_ROOT ) {
 				// "root" is allowed anywhere
 				return substr($name, -5) == '.deny' || $name == 'SYSTEM_ACCESS.READONLY' ? 0 : 1;
 			}
 
 			if ( !isset($cat_id) ) {
 				$cat_id = $this->Application->GetVar('m_cat_id');
 			}
 
 			if ( $type == 1 ) {
 				// "system" permission are always checked per "Home" category (ID = 0)
 				$cat_id = 0;
 			}
 			elseif ( "$cat_id" === "0" ) {
 				$cat_id = $this->Application->getBaseCategory();
 			}
 
 			// perm cache is build only based on records in db, that's why if permission is not explicitly denied, then
 			// that (perm cache creator) code thinks that it is allowed & adds corresponding record and code below will
 			// return incorrect results
 			if ( $user_id == $this->Application->RecallVar('user_id') ) {
 				$groups = $this->Application->RecallVar('UserGroups');
 			}
 			else {
 				// checking not current user
 				$groups = $this->Application->RecallVar('UserGroups:' . $user_id);
 
 				if ( $groups === false ) {
 //					die('me');
 					$sql = 'SELECT GroupId
 							FROM '.TABLE_PREFIX.'UserGroupRelations
 							WHERE (PortalUserId = '.$user_id.') AND ( (MembershipExpires IS NULL) OR ( MembershipExpires >= UNIX_TIMESTAMP() ) )';
 					$groups = $this->Conn->GetCol($sql);
 
 					array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup') );
 					$groups = implode(',', $groups);
 
 					$this->Application->StoreVar('UserGroups:' . $user_id, $groups);
 				}
 			}
 
 			$groups = explode(',', $groups);
 			$cache_key = $name . '|' . $type . '|' . $cat_id . '|' . implode(',', $groups);
 			$perm_value = $this->Application->getCache('permissions[%' . ($type == 1 ? 'G' : 'C') . 'PermSerial%]:' . $cache_key);
 
 			if ( $perm_value !== false ) {
 				return $perm_value;
 			}
 
 			if ( preg_match('/(.*)\.VIEW$/', $name) && ($type == 0) ) {
 				// cached view permission of category: begin
 				if ( $this->Application->ConfigValue('CheckViewPermissionsInCatalog') ) {
 					if ( strpos($cat_id, '|') !== false ) {
 						$category_path = explode('|', substr($cat_id, 1, -1));
 						$cat_id = end($category_path);
 					}
 
 					$sql = 'SELECT PermissionConfigId
 							FROM ' . TABLE_PREFIX . 'CategoryPermissionsConfig
 							WHERE PermissionName = ' . $this->Conn->qstr($name);
 					$perm_id = $this->Conn->GetOne($sql);
 
 					$sql = 'SELECT PermId
 							FROM ' . TABLE_PREFIX . 'CategoryPermissionsCache
 							WHERE (PermId = ' . $perm_id . ') AND (CategoryId = ' . (int)$cat_id . ')';
 
 					$view_filters = Array ();
 					foreach ($groups as $group) {
 						$view_filters[] = 'FIND_IN_SET(' . $group . ', ACL)';
 					}
 					$sql .= ' AND (' . implode(' OR ', $view_filters) . ')';
 					$perm_value = $this->Conn->GetOne($sql) ? 1 : 0;
 				}
 				else {
 					$perm_value = 1;
 				}
 
 				$this->Application->setCache('permissions[%CPermSerial%]:' . $cache_key, $perm_value);
 				return $perm_value;
 				// cached view permission of category: end
 			}
 
 			if ( is_numeric($cat_id) && $cat_id == 0 ) {
 				$cat_hierarchy = Array (0);
 			}
 			else {
 				if ( strpos($cat_id, '|') !== false ) {
 					$cat_hierarchy = $cat_id;
 				}
 				else {
 					$sql = 'SELECT ParentPath
 							FROM ' . $this->Application->getUnitOption('c', 'TableName') . '
 							WHERE CategoryId = ' . $cat_id;
 					$cat_hierarchy = $this->Conn->GetOne($sql);
 					if ( $cat_hierarchy === false ) {
 						// category was deleted, but reference to it stays in other tables -> data integrity is broken
 						$cat_hierarchy = '|' . $this->Application->getBaseCategory() . '|';
 					}
 				}
 
 				$cat_hierarchy = explode('|', substr($cat_hierarchy, 1, -1));
 				$cat_hierarchy = array_reverse($cat_hierarchy);
 				array_push($cat_hierarchy, 0);
 			}
 
 			$perm_value = 0;
 			$groups = implode(',', $groups);
 			foreach ($cat_hierarchy as $category_id) {
 				$sql = 'SELECT SUM(PermissionValue)
 						FROM ' . TABLE_PREFIX . 'Permissions
 						WHERE Permission = "' . $name . '" AND CatId = ' . $category_id . ' AND GroupId IN (' . $groups . ') AND Type = ' . $type;
 				$res = $this->Conn->GetOne($sql);
 
 				if ( $res !== false && !is_null($res) ) {
 					$perm_value = $res ? 1 : 0;
 					break;
 				}
 			}
 
 			$this->Application->setCache('permissions[%' . ($type == 1 ? 'G' : 'C') . 'PermSerial%]:' . $cache_key, $perm_value);
 
 			return $perm_value;
 		}
 
 		/**
 		 * Returns categories, where given permission is set to "1"
 		 *
 		 * @param string $permission_name
 		 * @return Array
 		 */
 		function getPermissionCategories($permission_name)
 		{
 			$groups = $this->Application->RecallVar('UserGroups');
 
 			// get categories, where given permission is explicitely defined
 			$sql = 'SELECT SUM(PermissionValue), CatId
 					FROM ' . TABLE_PREFIX . 'Permissions
 					WHERE Permission = "' . $permission_name . '" AND GroupId IN (' . $groups . ') AND Type = 0
 					GROUP BY CatId';
 			$permissions = $this->Conn->GetCol($sql, 'CatId');
 
 			// get all categories along with their parent path
 			$sql = 'SELECT ParentPath, CategoryId
 					FROM ' . TABLE_PREFIX . 'Categories';
 			$parent_paths = $this->Conn->GetCol($sql, 'CategoryId');
 
 			foreach ($parent_paths as $category_id => $parent_path) {
 				if (array_key_exists($category_id, $permissions)) {
 					// permission for given category is set explicitly
 					continue;
 				}
 
 				$perm_value = 0;
 				$parent_path = explode('|', substr($parent_path, 1, -1));
 				$parent_path = array_reverse($parent_path);
 				array_push($parent_path, 0);
 
 				foreach ($parent_path as $parent_category_id) {
 					if (array_key_exists($parent_category_id, $permissions)) {
 						$perm_value = $permissions[$parent_category_id] ? 1 : 0;
 						break;
 					}
 				}
 
 				$permissions[$category_id] = $perm_value;
 			}
 
 			// remove categories, where given permissions is denied
 			foreach ($permissions as $category_id => $perm_value) {
 				if (!$perm_value) {
 					unset($permissions[$category_id]);
 				}
 			}
 
 			return array_keys($permissions);
 		}
 
 		/**
 		 * Allows to check MODIFY & OWNER.MODFY +/- PENDING permission combinations on item
 		 *
 		 * @param int $owner_id user_id, that is owner of the item
 		 * @param int $category_id primary category of item
 		 * @param string $prefix prefix of item
 		 * @return int {0 - no MODIFY permission, 1 - has MODIFY permission, 2 - has MODIFY.PENDING permission}
 		 */
 		function ModifyCheckPermission($owner_id, $category_id, $prefix)
 		{
 			$perm_prefix = $this->Application->getUnitOption($prefix, 'PermItemPrefix');
 
 			$live_modify = $this->CheckPermission($perm_prefix.'.MODIFY', ptCATEGORY, $category_id);
 			if ($live_modify) {
 				return 1;
 			}
 			else if ($this->CheckPermission($perm_prefix.'.MODIFY.PENDING', ptCATEGORY, $category_id)) {
 				return 2;
 			}
 
 			if ($owner_id == $this->Application->RecallVar('user_id')) {
 				// user is item's OWNER -> check this permissions first
 				$live_modify = $this->CheckPermission($perm_prefix.'.OWNER.MODIFY', ptCATEGORY, $category_id);
 				if ($live_modify) {
 					return 1;
 				}
 				else if ($this->CheckPermission($perm_prefix.'.OWNER.MODIFY.PENDING', ptCATEGORY, $category_id)) {
 					return 2;
 				}
 			}
 
 			return 0;
 		}
 
 		/**
 		 * Allows to check DELETE & OWNER.DELETE permission combinations on item
 		 *
 		 * @param int $owner_id user_id, that is owner of the item
 		 * @param int $category_id primary category of item
 		 * @param string $prefix prefix of item
 		 * @return int {0 - no DELETE permission, 1 - has DELETE/OWNER.DELETE permission}
 		 */
 		function DeleteCheckPermission($owner_id, $category_id, $prefix)
 		{
 			$perm_prefix = $this->Application->getUnitOption($prefix, 'PermItemPrefix');
 
 			$live_delete = $this->CheckPermission($perm_prefix.'.DELETE', ptCATEGORY, $category_id);
 			if ($live_delete) {
 				return 1;
 			}
 
 			if ($owner_id == $this->Application->RecallVar('user_id')) {
 				// user is item's OWNER -> check this permissions first
 				$live_delete = $this->CheckPermission($perm_prefix.'.OWNER.DELETE', ptCATEGORY, $category_id);
 				if ($live_delete) {
 					return 1;
 				}
 			}
 
 			return 0;
 		}
 
 		/**
 		 * Allows to check ADD +/- PENDING permission combinations on item
 		 *
 		 * @param int $category_id primary category of item
 		 * @param string $prefix prefix of item
 		 * @return int {0 - no ADD permission, 1 - has ADD permission, 2 - has ADD.PENDING permission}
 		 */
 		function AddCheckPermission($category_id, $prefix)
 		{
 			$perm_prefix = $this->Application->getUnitOption($prefix, 'PermItemPrefix');
 
 			$live_add = $this->CheckPermission($perm_prefix.'.ADD', ptCATEGORY, $category_id);
 			if ($live_add) {
 				return 1;
 			}
 			else if ($this->CheckPermission($perm_prefix.'.ADD.PENDING', ptCATEGORY, $category_id)) {
 				return 2;
 			}
 
 			return 0;
 		}
 	}
\ No newline at end of file
Index: branches/5.2.x/core/units/configuration/configuration_event_handler.php
===================================================================
--- branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 16413)
+++ branches/5.2.x/core/units/configuration/configuration_event_handler.php	(revision 16414)
@@ -1,568 +1,562 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	class ConfigurationEventHandler extends kDBEventHandler  {
 
 		/**
 		 * Allows to override standard permission mapping
 		 *
 		 * @return void
 		 * @access protected
 		 * @see kEventHandler::$permMapping
 		 */
 		protected function mapPermissions()
 		{
 			parent::mapPermissions();
 
 			$permissions = Array (
 				'OnGenerateMaintenancePage' => Array ('self' => 'add|edit'),
 			);
 
 			$this->permMapping = array_merge($this->permMapping, $permissions);
 		}
 
 		/**
 		 * Changes permission section to one from REQUEST, not from config
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access public
 		 */
 		public function CheckPermission(kEvent $event)
 		{
 			$event->setEventParam('PermSection', $this->Application->GetVar('section'));
 			return parent::CheckPermission($event);
 		}
 
 		/**
 		 * Apply any custom changes to list's sql query
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 * @see kDBEventHandler::OnListBuild()
 		 */
 		protected function SetCustomQuery(kEvent $event)
 		{
 			parent::SetCustomQuery($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBList */
 
 			$module = $this->Application->GetVar('module');
 			$section = $this->Application->GetVar('section');
 
 			$object->addFilter('module_filter', '%1$s.ModuleOwner = ' . $this->Conn->qstr($module));
 			$object->addFilter('section_filter', '%1$s.Section = ' . $this->Conn->qstr($section));
 
 			$can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange');
 
 			if ( !$can_change && !$this->Application->isDebugMode() ) {
 				$object->addFilter('interface_change_filter', '%1$s.VariableName NOT IN ("AdminConsoleInterface", "AllowAdminConsoleInterfaceChange")');
 			}
 
 			if ( defined('IS_INSTALL') && IS_INSTALL ) {
 				$object->addFilter('install_filter', '%1$s.Install = 1');
 			}
 
 			$object->addFilter('visible_filter', '%1$s.Heading <> ""');
 		}
 
 		/**
 		 * Presets new system setting fields
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnPreCreate(kEvent $event)
 		{
 			parent::OnPreCreate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$object->SetDBField('Section', $this->Application->GetVar('section'));
 			$object->SetDBField('ModuleOwner', $this->Application->GetVar('module'));
 		}
 
 		/**
 		 * Sets custom validation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemLoad(kEvent $event)
 		{
 			static $default_field_options = null;
 
 			parent::OnAfterItemLoad($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// ability to validate each configuration variable separately
 			if ( !isset($default_field_options) ) {
 				$default_field_options = $object->GetFieldOptions('VariableValue');
 			}
 
 			$new_field_options = $default_field_options;
 			$validation = $object->GetDBField('Validation');
 
 			if ( $validation ) {
 				$new_field_options = array_merge($new_field_options, unserialize($validation));
 			}
 
 			$object->SetFieldOptions('VariableValue', $new_field_options);
 		}
 
 		/**
 		 * Performs custom validation
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemValidate(kEvent $event)
 		{
 			parent::OnBeforeItemValidate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// if password field is empty, then don't update
 			if ( $object->GetDBField('ElementType') == 'password' ) {
 				if ( trim($object->GetDBField('VariableValue')) != '' ) {
 					$password_formatter = $this->Application->recallObject('kPasswordFormatter');
 					/* @var $password_formatter kPasswordFormatter */
 
 					$object->SetDBField('VariableValue', $password_formatter->hashPassword($object->GetDBField('VariableValue')));
 				}
 			}
 
 			$this->_processCountryState($event);
 
 			$variable_name = $object->GetDBField('VariableName');
 			$new_value = $object->GetDBField('VariableValue');
 
 			if ( $variable_name == 'AdminConsoleInterface' ) {
 				$can_change = $this->Application->ConfigValue('AllowAdminConsoleInterfaceChange');
 
 				if ( ($new_value != $object->GetOriginalField('VariableValue')) && !$can_change ) {
 					$object->SetError('VariableValue', 'not_allowed', 'la_error_OperationNotAllowed');
 				}
 			}
 			elseif ( $variable_name == 'HardMaintenanceTemplate' ) {
 				$compile = $event->MasterEvent->getEventParam('compile_maintenance_template');
 				$compile = $compile || $new_value != $object->GetOriginalField('VariableValue');
 
 				if ( $compile && !$this->_generateMaintenancePage($new_value) ) {
 					$object->SetError('VariableValue', 'template_file_missing', 'la_error_TemplateFileMissing');
 				}
 			}
 			elseif ( $variable_name == 'DefaultEmailRecipients' ) {
 				$email_event_data = $this->Application->GetVar('email-template_' . $event->Prefix);
 				$object->SetDBField('VariableValue', $email_event_data[0]['Recipients']);
 			}
 
 			$sections_helper = $this->Application->recallObject('SectionsHelper');
 			/* @var $sections_helper kSectionsHelper */
 
 			$section = $object->GetDBField('Section');
 
 			if ( $section && !$sections_helper->getSectionData($section) ) {
 				$object->SetError('Section', 'unknown_section');
 			}
 		}
 
 		/**
 		 * Checks, that state belongs to selected country
 		 *
 		 * @param kEvent $event
 		 * @access protected
 		 */
 		protected function _processCountryState(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$country_iso = $this->_getCountryByState($event);
 			$state_name = $object->GetDBField('VariableValue');
 
 			if ( !$country_iso || !$state_name ) {
 				return;
 			}
 
 			$cs_helper = $this->Application->recallObject('CountryStatesHelper');
 			/* @var $cs_helper kCountryStatesHelper */
 
 			$state_iso = $cs_helper->getStateIso($state_name, $country_iso);
 
 			if ( $state_iso !== false ) {
 				$object->SetDBField('VariableValue', $state_iso);
 			}
 			else {
 				// selected state doesn't belong to selected country
 				$object->SetError('VariableValue', 'invalid_state', 'la_InvalidState');
 			}
 		}
 
 		/**
 		 * Returns country iso code, that matches current state variable name
 		 *
 		 * @param kEvent $event
 		 * @return bool
 		 * @access protected
 		 */
 		protected function _getCountryByState(kEvent $event)
 		{
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			$variable_name = $object->GetDBField('VariableName');
 
 			$state_country_hash = Array (
 				'Comm_State' => 'Comm_Country',
 				'Comm_Shipping_State' => 'Comm_Shipping_Country'
 			);
 
 			if ( !array_key_exists($variable_name, $state_country_hash) ) {
 				return false;
 			}
 
 			$field_values = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			$sql = 'SELECT VariableId
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE VariableName = ' . $this->Conn->qstr($state_country_hash[$variable_name]);
 			$country_variable_id = $this->Conn->GetOne($sql);
 
 			return $field_values[$country_variable_id]['VariableValue'];
 		}
 
 		/**
 		 * Does custom password setting processong
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnBeforeItemUpdate(kEvent $event)
 		{
 			parent::OnBeforeItemUpdate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			// if password field is empty, then don't update
 			if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) {
 				$object->SetFieldOption('VariableValue', 'skip_empty', 1);
 			}
 		}
 
 		/**
 		 * Occurs after updating item
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterItemUpdate(kEvent $event)
 		{
 			static $skin_deleted = false;
 
 			parent::OnAfterItemUpdate($event);
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( $object->GetDBField('ElementType') == 'password' && trim($object->GetDBField('VariableValue')) == '' ) {
 				$object->SetFieldOption('VariableValue', 'skip_empty', 0);
 			}
 
 			// allows to check if variable's value was changed now
 			$variable_name = $object->GetDBField('VariableName');
 			$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 
 			if ( $object->GetDBField('VariableValue') != $object->GetOriginalField('VariableValue') ) {
 				$changed[] = $variable_name;
 				$this->Application->SetVar($event->getPrefixSpecial() . '_changed', $changed);
 
 				// update value in cache, so other code (during this script run) would use new value
 				$this->Application->SetConfigValue($variable_name, $object->GetDBField('VariableValue'), true);
 			}
 
 			if ( $variable_name == 'Require_AdminSSL' || $variable_name == 'AdminSSL_URL' ) {
 				// when administrative console is moved to SSL mode, then delete skin
 				if ( in_array($variable_name, $changed) && !$skin_deleted ) {
 					$skin_helper = $this->Application->recallObject('SkinHelper');
 					/* @var $skin_helper SkinHelper */
 
 					$skin_file = $skin_helper->getSkinPath();
 					if ( file_exists($skin_file) ) {
 						unlink($skin_file);
 					}
 
 					$skin_deleted = true;
 				}
 			}
 		}
 
 		/**
 		 * Updates kDBItem
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnUpdate(kEvent $event)
 		{
 			if ( $this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1) ) {
 				$event->status = kEvent::erFAIL;
 				return;
 			}
 
 			$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
 
 			// 1. save user selected module root category
 			$new_category_id = getArrayValue($items_info, 'ModuleRootCategory', 'VariableValue');
 			if ( $new_category_id !== false ) {
 				unset($items_info['ModuleRootCategory']);
 			}
 
 			$object = $event->getObject( Array('skip_autoload' => true) );
 			/* @var $object kDBItem */
 
 			if ( $items_info ) {
 				$has_error = false;
 
 				foreach ($items_info as $id => $field_values) {
 					$object->Clear(); // clear validation errors from previous variable
 					$object->Load($id);
 					$object->SetFieldsFromHash($field_values);
 					$event->setEventParam('form_data', $field_values);
 
 					if ( !$object->Update($id) ) {
 						// don't stop when error found !
 						$has_error = true;
 					}
 				}
 
 				$event->status = $has_error ? kEvent::erFAIL : kEvent::erSUCCESS;
 			}
 
 			if ( $event->status == kEvent::erSUCCESS ) {
 				$event->SetRedirectParam('action_completed', 1);
 
 				if ( $new_category_id !== false ) {
 					// root category was submitted
 					$module = $this->Application->GetVar('module');
 					$root_category_id = $this->Application->findModule('Name', $module, 'RootCat');
 
 					if ( $root_category_id != $new_category_id ) {
 						// root category differs from one in db
 						$fields_hash = Array ('RootCat' => $new_category_id);
 						$this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'Modules', 'Name = ' . $this->Conn->qstr($module));
 					}
 				}
 
 				// reset cache
 				$changed = $this->Application->GetVar($event->getPrefixSpecial() . '_changed', Array ());
 				$require_refresh = Array ('AdvancedUserManagement', 'Site_Name', 'AdminConsoleInterface', 'UsePopups', 'Catalog_PreselectModuleTab');
 
 				$refresh_sections = array_intersect($require_refresh, $changed);
 				$require_full_refresh = Array ('Site_Name', 'AdminConsoleInterface');
 
 				if ( array_intersect($require_full_refresh, $changed) ) {
 					$event->SetRedirectParam('refresh_all', 1);
 				}
 				elseif ( $refresh_sections ) {
 					$event->SetRedirectParam('refresh_tree', 1);
 				}
 
 				if ( $refresh_sections ) {
 					// reset sections too, because of AdvancedUserManagement
 					$this->Application->DeleteSectionCache();
 				}
 
 				$this->Application->DeleteUnitCache($changed);
 			}
 			else{
 				$errors = $this->Application->GetVar('errors_' . $event->getPrefixSpecial());
 
 				if ( $errors ) {
 					// because we have list out there, and this is item
 					$this->Application->SetVar('first_error', key($errors));
 					$this->Application->removeObject($event->getPrefixSpecial());
 				}
 			}
 
 			// keeps module and section in REQUEST to ensure, that last admin template will work
 			$event->SetRedirectParam('module', $this->Application->GetVar('module'));
 			$event->SetRedirectParam('section', $this->Application->GetVar('section'));
 		}
 
 		/**
 		 * Process items from selector (selected_ids var, key - prefix, value - comma separated ids)
 		 *
 		 * @param kEvent $event
 		 */
 		function OnProcessSelected($event)
 		{
 			$selected_ids = $this->Application->GetVar('selected_ids');
 			$this->Application->StoreVar('ModuleRootCategory', $selected_ids['c']);
 
 			$event->SetRedirectParam('opener', 'u');
 		}
 
 		/**
 		 * Generates maintenance page
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnGenerateMaintenancePage(kEvent $event)
 		{
 			$event->setEventParam('compile_maintenance_template', 1);
 
 			$event->CallSubEvent('OnUpdate');
 		}
 
 		/**
 		 * Generates HTML version of hard maintenance template
 		 *
 		 * @param string $template
 		 * @return bool
 		 * @access protected
 		 */
 		protected function _generateMaintenancePage($template = null)
 		{
 			if ( !isset($template) ) {
 				$template = $this->Application->ConfigValue('HardMaintenanceTemplate');
 			}
 
 			$curl_helper = $this->Application->recallObject('CurlHelper');
 			/* @var $curl_helper kCurlHelper */
 
 			$html = $curl_helper->Send($this->Application->BaseURL() . '?t=' . $template);
 
 			if ( $curl_helper->isGoodResponseCode() ) {
 				file_put_contents(WRITEABLE . DIRECTORY_SEPARATOR . 'maintenance.html', $html);
 
 				return true;
 			}
 
 			return false;
 		}
 
 		/**
 		 * Returns auto-complete values for ajax-dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnSuggestValues(kEvent $event)
 		{
-			if ( !$this->Application->isAdminUser ) {
-				// very careful here, because this event allows to
-				// view every object field -> limit only to logged-in admins
-				return;
-			}
-
 			$event->status = kEvent::erSTOP;
 
 			$field = $this->Application->GetVar('field');
 			$cur_value = $this->Application->GetVar('cur_value');
 
 			$object = $event->getObject();
 			/* @var $object kDBItem */
 
 			if ( !$field || !$cur_value || !$object->isField($field) ) {
 				return;
 			}
 
 			$limit = $this->Application->GetVar('limit');
 			if ( !$limit ) {
 				$limit = 20;
 			}
 
 			$sql = 'SELECT DISTINCT ' . $field . ', ModuleOwner
 					FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
 					WHERE ' . $field . ' LIKE ' . $this->Conn->qstr('%' . $cur_value . '%') . '
 					ORDER BY ' . $field . ' ASC';
 			$raw_suggestions = $this->Conn->Query($sql);
 
 			$suggestions = Array ();
 			$this->Application->XMLHeader();
 
 			foreach ($raw_suggestions as $raw_suggestion) {
 				$suggestion = $raw_suggestion[$field];
 
 				if ( !isset($suggestions[$suggestion]) ) {
 					$suggestions[$suggestion] = Array ();
 				}
 
 				$suggestions[$suggestion][] = $raw_suggestion['ModuleOwner'];
 			}
 
 			array_splice($suggestions, $limit);
 
 			echo '<suggestions>';
 			$of_label = $this->Application->Phrase('la_From', false);
 
 			foreach ($suggestions as $suggestion_value => $suggestion_modules) {
 				$suggestion_module = in_array('In-Portal', $suggestion_modules) ? 'In-Portal' : implode(', ', $suggestion_modules);
 				$suggestion_title = $suggestion_value . ' <em style="color: grey;">' . $of_label . ' ' . $suggestion_module . '</em>';
 
 				echo '<item value="' . kUtil::escape($suggestion_value, kUtil::ESCAPE_HTML) . '">' . kUtil::escape($suggestion_title, kUtil::ESCAPE_HTML) . '</item>';
 			}
 
 			echo '</suggestions>';
 		}
 
 		/**
 		 * Prefills module dropdown
 		 *
 		 * @param kEvent $event
 		 * @return void
 		 * @access protected
 		 */
 		protected function OnAfterConfigRead(kEvent $event)
 		{
 			parent::OnAfterConfigRead($event);
 
 			$options = Array ();
 
 			foreach ($this->Application->ModuleInfo as $module_name => $module_info) {
 				if ( $module_name == 'Core' ) {
 					continue;
 				}
 
 				$options[$module_name] = $module_name;
 
 				if ( $module_name == 'In-Portal' ) {
 					$options['In-Portal:Users'] = 'In-Portal:Users';
 				}
 			}
 
 			$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
 			$fields['ModuleOwner']['options'] = $options;
 			$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
 		}
 	}
Index: branches/5.2.x/CREDITS
===================================================================
--- branches/5.2.x/CREDITS	(revision 16413)
+++ branches/5.2.x/CREDITS	(revision 16414)

Property changes on: branches/5.2.x/CREDITS
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/CREDITS:r15987
Index: branches/5.2.x/README
===================================================================
--- branches/5.2.x/README	(revision 16413)
+++ branches/5.2.x/README	(revision 16414)

Property changes on: branches/5.2.x/README
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/README:r15987
Index: branches/5.2.x/index.php
===================================================================
--- branches/5.2.x/index.php	(revision 16413)
+++ branches/5.2.x/index.php	(revision 16414)

Property changes on: branches/5.2.x/index.php
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/index.php:r15987
Index: branches/5.2.x/LICENSES
===================================================================
--- branches/5.2.x/LICENSES	(revision 16413)
+++ branches/5.2.x/LICENSES	(revision 16414)

Property changes on: branches/5.2.x/LICENSES
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/LICENSES:r15987
Index: branches/5.2.x/INSTALL
===================================================================
--- branches/5.2.x/INSTALL	(revision 16413)
+++ branches/5.2.x/INSTALL	(revision 16414)

Property changes on: branches/5.2.x/INSTALL
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/INSTALL:r15987
Index: branches/5.2.x/COPYRIGHT
===================================================================
--- branches/5.2.x/COPYRIGHT	(revision 16413)
+++ branches/5.2.x/COPYRIGHT	(revision 16414)

Property changes on: branches/5.2.x/COPYRIGHT
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/COPYRIGHT:r15987
Index: branches/5.2.x/.htaccess
===================================================================
--- branches/5.2.x/.htaccess	(revision 16413)
+++ branches/5.2.x/.htaccess	(revision 16414)

Property changes on: branches/5.2.x/.htaccess
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x/.htaccess:r15987
Index: branches/5.2.x
===================================================================
--- branches/5.2.x	(revision 16413)
+++ branches/5.2.x	(revision 16414)

Property changes on: branches/5.2.x
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /in-portal/branches/5.3.x:r15987